ntb_hw_intel.c revision 302531
1250079Scarl/*-
2302484Smav * Copyright (c) 2016 Alexander Motin <mav@FreeBSD.org>
3250079Scarl * Copyright (C) 2013 Intel Corporation
4289542Scem * Copyright (C) 2015 EMC Corporation
5250079Scarl * All rights reserved.
6250079Scarl *
7250079Scarl * Redistribution and use in source and binary forms, with or without
8250079Scarl * modification, are permitted provided that the following conditions
9250079Scarl * are met:
10250079Scarl * 1. Redistributions of source code must retain the above copyright
11250079Scarl *    notice, this list of conditions and the following disclaimer.
12250079Scarl * 2. Redistributions in binary form must reproduce the above copyright
13250079Scarl *    notice, this list of conditions and the following disclaimer in the
14250079Scarl *    documentation and/or other materials provided with the distribution.
15250079Scarl *
16250079Scarl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17250079Scarl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18250079Scarl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19250079Scarl * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20250079Scarl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21250079Scarl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22250079Scarl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23250079Scarl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24250079Scarl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25250079Scarl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26250079Scarl * SUCH DAMAGE.
27250079Scarl */
28250079Scarl
29302484Smav/*
30302484Smav * The Non-Transparent Bridge (NTB) is a device that allows you to connect
31302484Smav * two or more systems using a PCI-e links, providing remote memory access.
32302484Smav *
33302484Smav * This module contains a driver for NTB hardware in Intel Xeon/Atom CPUs.
34302484Smav *
35302484Smav * NOTE: Much of the code in this module is shared with Linux. Any patches may
36302484Smav * be picked up and redistributed in Linux with a dual GPL/BSD license.
37302484Smav */
38302484Smav
39250079Scarl#include <sys/cdefs.h>
40250079Scarl__FBSDID("$FreeBSD: head/sys/dev/ntb/ntb_hw/ntb_hw.c 302531 2016-07-10 20:22:04Z mav $");
41250079Scarl
42250079Scarl#include <sys/param.h>
43250079Scarl#include <sys/kernel.h>
44250079Scarl#include <sys/systm.h>
45250079Scarl#include <sys/bus.h>
46289774Scem#include <sys/endian.h>
47302493Smav#include <sys/interrupt.h>
48250079Scarl#include <sys/malloc.h>
49250079Scarl#include <sys/module.h>
50295618Scem#include <sys/mutex.h>
51295618Scem#include <sys/pciio.h>
52250079Scarl#include <sys/queue.h>
53250079Scarl#include <sys/rman.h>
54302491Smav#include <sys/rmlock.h>
55289774Scem#include <sys/sbuf.h>
56289207Scem#include <sys/sysctl.h>
57250079Scarl#include <vm/vm.h>
58250079Scarl#include <vm/pmap.h>
59250079Scarl#include <machine/bus.h>
60295618Scem#include <machine/intr_machdep.h>
61250079Scarl#include <machine/resource.h>
62250079Scarl#include <dev/pci/pcireg.h>
63250079Scarl#include <dev/pci/pcivar.h>
64250079Scarl
65250079Scarl#include "ntb_regs.h"
66302484Smav#include "../ntb.h"
67250079Scarl
68289648Scem#define MAX_MSIX_INTERRUPTS MAX(XEON_DB_COUNT, ATOM_DB_COUNT)
69250079Scarl
70289539Scem#define NTB_HB_TIMEOUT		1 /* second */
71289648Scem#define ATOM_LINK_RECOVERY_TIME	500 /* ms */
72291032Scem#define BAR_HIGH_MASK		(~((1ull << 12) - 1))
73250079Scarl
74295618Scem#define	NTB_MSIX_VER_GUARD	0xaabbccdd
75295618Scem#define	NTB_MSIX_RECEIVED	0xe0f0e0f0
76295618Scem
77295618Scem/*
78295618Scem * PCI constants could be somewhere more generic, but aren't defined/used in
79295618Scem * pci.c.
80295618Scem */
81295618Scem#define	PCI_MSIX_ENTRY_SIZE		16
82295618Scem#define	PCI_MSIX_ENTRY_LOWER_ADDR	0
83295618Scem#define	PCI_MSIX_ENTRY_UPPER_ADDR	4
84295618Scem#define	PCI_MSIX_ENTRY_DATA		8
85295618Scem
86250079Scarlenum ntb_device_type {
87250079Scarl	NTB_XEON,
88289648Scem	NTB_ATOM
89250079Scarl};
90250079Scarl
91289610Scem/* ntb_conn_type are hardware numbers, cannot change. */
92289610Scemenum ntb_conn_type {
93289610Scem	NTB_CONN_TRANSPARENT = 0,
94289610Scem	NTB_CONN_B2B = 1,
95289610Scem	NTB_CONN_RP = 2,
96289610Scem};
97289610Scem
98289610Scemenum ntb_b2b_direction {
99289610Scem	NTB_DEV_USD = 0,
100289610Scem	NTB_DEV_DSD = 1,
101289610Scem};
102289610Scem
103289539Scemenum ntb_bar {
104289539Scem	NTB_CONFIG_BAR = 0,
105289539Scem	NTB_B2B_BAR_1,
106289539Scem	NTB_B2B_BAR_2,
107289539Scem	NTB_B2B_BAR_3,
108289539Scem	NTB_MAX_BARS
109289539Scem};
110289539Scem
111295618Scemenum {
112295618Scem	NTB_MSIX_GUARD = 0,
113295618Scem	NTB_MSIX_DATA0,
114295618Scem	NTB_MSIX_DATA1,
115295618Scem	NTB_MSIX_DATA2,
116295618Scem	NTB_MSIX_OFS0,
117295618Scem	NTB_MSIX_OFS1,
118295618Scem	NTB_MSIX_OFS2,
119295618Scem	NTB_MSIX_DONE,
120295618Scem	NTB_MAX_MSIX_SPAD
121295618Scem};
122295618Scem
123255274Scarl/* Device features and workarounds */
124302484Smav#define HAS_FEATURE(ntb, feature)	\
125302484Smav	(((ntb)->features & (feature)) != 0)
126255274Scarl
127250079Scarlstruct ntb_hw_info {
128250079Scarl	uint32_t		device_id;
129255274Scarl	const char		*desc;
130250079Scarl	enum ntb_device_type	type;
131289397Scem	uint32_t		features;
132250079Scarl};
133250079Scarl
134250079Scarlstruct ntb_pci_bar_info {
135250079Scarl	bus_space_tag_t		pci_bus_tag;
136250079Scarl	bus_space_handle_t	pci_bus_handle;
137250079Scarl	int			pci_resource_id;
138250079Scarl	struct resource		*pci_resource;
139250079Scarl	vm_paddr_t		pbase;
140290679Scem	caddr_t			vbase;
141290679Scem	vm_size_t		size;
142291280Scem	vm_memattr_t		map_mode;
143289543Scem
144289543Scem	/* Configuration register offsets */
145289543Scem	uint32_t		psz_off;
146289543Scem	uint32_t		ssz_off;
147289543Scem	uint32_t		pbarxlat_off;
148250079Scarl};
149250079Scarl
150250079Scarlstruct ntb_int_info {
151250079Scarl	struct resource	*res;
152250079Scarl	int		rid;
153250079Scarl	void		*tag;
154250079Scarl};
155250079Scarl
156289546Scemstruct ntb_vec {
157250079Scarl	struct ntb_softc	*ntb;
158289546Scem	uint32_t		num;
159295618Scem	unsigned		masked;
160250079Scarl};
161250079Scarl
162289542Scemstruct ntb_reg {
163289542Scem	uint32_t	ntb_ctl;
164289542Scem	uint32_t	lnk_sta;
165289542Scem	uint8_t		db_size;
166289542Scem	unsigned	mw_bar[NTB_MAX_BARS];
167289542Scem};
168289542Scem
169289542Scemstruct ntb_alt_reg {
170289542Scem	uint32_t	db_bell;
171289542Scem	uint32_t	db_mask;
172289542Scem	uint32_t	spad;
173289542Scem};
174289542Scem
175289542Scemstruct ntb_xlat_reg {
176289546Scem	uint32_t	bar0_base;
177289546Scem	uint32_t	bar2_base;
178289546Scem	uint32_t	bar4_base;
179289546Scem	uint32_t	bar5_base;
180289546Scem
181289546Scem	uint32_t	bar2_xlat;
182289546Scem	uint32_t	bar4_xlat;
183289546Scem	uint32_t	bar5_xlat;
184289546Scem
185289546Scem	uint32_t	bar2_limit;
186289546Scem	uint32_t	bar4_limit;
187289546Scem	uint32_t	bar5_limit;
188289542Scem};
189289542Scem
190289542Scemstruct ntb_b2b_addr {
191289542Scem	uint64_t	bar0_addr;
192289542Scem	uint64_t	bar2_addr64;
193289542Scem	uint64_t	bar4_addr64;
194289542Scem	uint64_t	bar4_addr32;
195289542Scem	uint64_t	bar5_addr32;
196289542Scem};
197289542Scem
198295618Scemstruct ntb_msix_data {
199295618Scem	uint32_t	nmd_ofs;
200295618Scem	uint32_t	nmd_data;
201295618Scem};
202295618Scem
203250079Scarlstruct ntb_softc {
204250079Scarl	device_t		device;
205250079Scarl	enum ntb_device_type	type;
206289774Scem	uint32_t		features;
207250079Scarl
208250079Scarl	struct ntb_pci_bar_info	bar_info[NTB_MAX_BARS];
209250079Scarl	struct ntb_int_info	int_info[MAX_MSIX_INTERRUPTS];
210250079Scarl	uint32_t		allocated_interrupts;
211250079Scarl
212295618Scem	struct ntb_msix_data	peer_msix_data[XEON_NONLINK_DB_MSIX_BITS];
213295618Scem	struct ntb_msix_data	msix_data[XEON_NONLINK_DB_MSIX_BITS];
214295618Scem	bool			peer_msix_good;
215295618Scem	bool			peer_msix_done;
216295618Scem	struct ntb_pci_bar_info	*peer_lapic_bar;
217295618Scem	struct callout		peer_msix_work;
218295618Scem
219250079Scarl	struct callout		heartbeat_timer;
220250079Scarl	struct callout		lr_timer;
221250079Scarl
222289546Scem	void			*ntb_ctx;
223289546Scem	const struct ntb_ctx_ops *ctx_ops;
224289546Scem	struct ntb_vec		*msix_vec;
225302491Smav	struct rmlock		ctx_lock;
226250079Scarl
227289610Scem	uint32_t		ppd;
228289610Scem	enum ntb_conn_type	conn_type;
229289610Scem	enum ntb_b2b_direction	dev_type;
230289539Scem
231289542Scem	/* Offset of peer bar0 in B2B BAR */
232289542Scem	uint64_t			b2b_off;
233289542Scem	/* Memory window used to access peer bar0 */
234289543Scem#define B2B_MW_DISABLED			UINT8_MAX
235289542Scem	uint8_t				b2b_mw_idx;
236301293Smav	uint32_t			msix_xlat;
237295618Scem	uint8_t				msix_mw_idx;
238289542Scem
239289539Scem	uint8_t				mw_count;
240289539Scem	uint8_t				spad_count;
241289539Scem	uint8_t				db_count;
242289539Scem	uint8_t				db_vec_count;
243289539Scem	uint8_t				db_vec_shift;
244289542Scem
245289546Scem	/* Protects local db_mask. */
246289546Scem#define DB_MASK_LOCK(sc)	mtx_lock_spin(&(sc)->db_mask_lock)
247289546Scem#define DB_MASK_UNLOCK(sc)	mtx_unlock_spin(&(sc)->db_mask_lock)
248289546Scem#define DB_MASK_ASSERT(sc,f)	mtx_assert(&(sc)->db_mask_lock, (f))
249289542Scem	struct mtx			db_mask_lock;
250289542Scem
251290686Scem	volatile uint32_t		ntb_ctl;
252290686Scem	volatile uint32_t		lnk_sta;
253289542Scem
254289542Scem	uint64_t			db_valid_mask;
255289542Scem	uint64_t			db_link_mask;
256289546Scem	uint64_t			db_mask;
257302493Smav	uint64_t			fake_db_bell;	/* NTB_SB01BASE_LOCKUP*/
258289542Scem
259289542Scem	int				last_ts;	/* ticks @ last irq */
260289542Scem
261289542Scem	const struct ntb_reg		*reg;
262289542Scem	const struct ntb_alt_reg	*self_reg;
263289542Scem	const struct ntb_alt_reg	*peer_reg;
264289542Scem	const struct ntb_xlat_reg	*xlat_reg;
265250079Scarl};
266250079Scarl
267289234Scem#ifdef __i386__
268289234Scemstatic __inline uint64_t
269289234Scembus_space_read_8(bus_space_tag_t tag, bus_space_handle_t handle,
270289234Scem    bus_size_t offset)
271289234Scem{
272289234Scem
273289234Scem	return (bus_space_read_4(tag, handle, offset) |
274289234Scem	    ((uint64_t)bus_space_read_4(tag, handle, offset + 4)) << 32);
275289234Scem}
276289234Scem
277289234Scemstatic __inline void
278289234Scembus_space_write_8(bus_space_tag_t tag, bus_space_handle_t handle,
279289234Scem    bus_size_t offset, uint64_t val)
280289234Scem{
281289234Scem
282289234Scem	bus_space_write_4(tag, handle, offset, val);
283289234Scem	bus_space_write_4(tag, handle, offset + 4, val >> 32);
284289234Scem}
285289234Scem#endif
286289234Scem
287255279Scarl#define ntb_bar_read(SIZE, bar, offset) \
288255279Scarl	    bus_space_read_ ## SIZE (ntb->bar_info[(bar)].pci_bus_tag, \
289255279Scarl	    ntb->bar_info[(bar)].pci_bus_handle, (offset))
290255279Scarl#define ntb_bar_write(SIZE, bar, offset, val) \
291255279Scarl	    bus_space_write_ ## SIZE (ntb->bar_info[(bar)].pci_bus_tag, \
292255279Scarl	    ntb->bar_info[(bar)].pci_bus_handle, (offset), (val))
293255279Scarl#define ntb_reg_read(SIZE, offset) ntb_bar_read(SIZE, NTB_CONFIG_BAR, offset)
294250079Scarl#define ntb_reg_write(SIZE, offset, val) \
295255279Scarl	    ntb_bar_write(SIZE, NTB_CONFIG_BAR, offset, val)
296289397Scem#define ntb_mw_read(SIZE, offset) \
297289542Scem	    ntb_bar_read(SIZE, ntb_mw_to_bar(ntb, ntb->b2b_mw_idx), offset)
298255279Scarl#define ntb_mw_write(SIZE, offset, val) \
299289542Scem	    ntb_bar_write(SIZE, ntb_mw_to_bar(ntb, ntb->b2b_mw_idx), \
300289397Scem		offset, val)
301250079Scarl
302250079Scarlstatic int ntb_probe(device_t device);
303250079Scarlstatic int ntb_attach(device_t device);
304250079Scarlstatic int ntb_detach(device_t device);
305302484Smavstatic uint64_t ntb_db_valid_mask(device_t dev);
306302484Smavstatic void ntb_spad_clear(device_t dev);
307302484Smavstatic uint64_t ntb_db_vector_mask(device_t dev, uint32_t vector);
308302484Smavstatic bool ntb_link_is_up(device_t dev, enum ntb_speed *speed,
309302484Smav    enum ntb_width *width);
310302484Smavstatic int ntb_link_enable(device_t dev, enum ntb_speed speed,
311302484Smav    enum ntb_width width);
312302484Smavstatic int ntb_link_disable(device_t dev);
313302484Smavstatic int ntb_spad_read(device_t dev, unsigned int idx, uint32_t *val);
314302484Smavstatic int ntb_peer_spad_write(device_t dev, unsigned int idx, uint32_t val);
315302484Smav
316291263Scemstatic unsigned ntb_user_mw_to_idx(struct ntb_softc *, unsigned uidx);
317289539Scemstatic inline enum ntb_bar ntb_mw_to_bar(struct ntb_softc *, unsigned mw);
318289546Scemstatic inline bool bar_is_64bit(struct ntb_softc *, enum ntb_bar);
319289546Scemstatic inline void bar_get_xlat_params(struct ntb_softc *, enum ntb_bar,
320289546Scem    uint32_t *base, uint32_t *xlat, uint32_t *lmt);
321255272Scarlstatic int ntb_map_pci_bars(struct ntb_softc *ntb);
322291280Scemstatic int ntb_mw_set_wc_internal(struct ntb_softc *, unsigned idx,
323291280Scem    vm_memattr_t);
324289647Scemstatic void print_map_success(struct ntb_softc *, struct ntb_pci_bar_info *,
325289647Scem    const char *);
326255272Scarlstatic int map_mmr_bar(struct ntb_softc *ntb, struct ntb_pci_bar_info *bar);
327255272Scarlstatic int map_memory_window_bar(struct ntb_softc *ntb,
328255272Scarl    struct ntb_pci_bar_info *bar);
329250079Scarlstatic void ntb_unmap_pci_bar(struct ntb_softc *ntb);
330289344Scemstatic int ntb_remap_msix(device_t, uint32_t desired, uint32_t avail);
331289540Scemstatic int ntb_init_isr(struct ntb_softc *ntb);
332289342Scemstatic int ntb_setup_legacy_interrupt(struct ntb_softc *ntb);
333289540Scemstatic int ntb_setup_msix(struct ntb_softc *ntb, uint32_t num_vectors);
334250079Scarlstatic void ntb_teardown_interrupts(struct ntb_softc *ntb);
335289540Scemstatic inline uint64_t ntb_vec_mask(struct ntb_softc *, uint64_t db_vector);
336289546Scemstatic void ntb_interrupt(struct ntb_softc *, uint32_t vec);
337289546Scemstatic void ndev_vec_isr(void *arg);
338289546Scemstatic void ndev_irq_isr(void *arg);
339289546Scemstatic inline uint64_t db_ioread(struct ntb_softc *, uint64_t regoff);
340290678Scemstatic inline void db_iowrite(struct ntb_softc *, uint64_t regoff, uint64_t);
341290678Scemstatic inline void db_iowrite_raw(struct ntb_softc *, uint64_t regoff, uint64_t);
342289546Scemstatic int ntb_create_msix_vec(struct ntb_softc *ntb, uint32_t num_vectors);
343289546Scemstatic void ntb_free_msix_vec(struct ntb_softc *ntb);
344300531Scemstatic void ntb_get_msix_info(struct ntb_softc *ntb);
345295618Scemstatic void ntb_exchange_msix(void *);
346250079Scarlstatic struct ntb_hw_info *ntb_get_device_info(uint32_t device_id);
347289397Scemstatic void ntb_detect_max_mw(struct ntb_softc *ntb);
348289348Scemstatic int ntb_detect_xeon(struct ntb_softc *ntb);
349289648Scemstatic int ntb_detect_atom(struct ntb_softc *ntb);
350289542Scemstatic int ntb_xeon_init_dev(struct ntb_softc *ntb);
351289648Scemstatic int ntb_atom_init_dev(struct ntb_softc *ntb);
352289272Scemstatic void ntb_teardown_xeon(struct ntb_softc *ntb);
353289648Scemstatic void configure_atom_secondary_side_bars(struct ntb_softc *ntb);
354289543Scemstatic void xeon_reset_sbar_size(struct ntb_softc *, enum ntb_bar idx,
355289543Scem    enum ntb_bar regbar);
356289543Scemstatic void xeon_set_sbar_base_and_limit(struct ntb_softc *,
357289543Scem    uint64_t base_addr, enum ntb_bar idx, enum ntb_bar regbar);
358289543Scemstatic void xeon_set_pbar_xlat(struct ntb_softc *, uint64_t base_addr,
359289543Scem    enum ntb_bar idx);
360289542Scemstatic int xeon_setup_b2b_mw(struct ntb_softc *,
361289542Scem    const struct ntb_b2b_addr *addr, const struct ntb_b2b_addr *peer_addr);
362289546Scemstatic inline bool link_is_up(struct ntb_softc *ntb);
363295618Scemstatic inline bool _xeon_link_is_up(struct ntb_softc *ntb);
364289648Scemstatic inline bool atom_link_is_err(struct ntb_softc *ntb);
365289546Scemstatic inline enum ntb_speed ntb_link_sta_speed(struct ntb_softc *);
366289546Scemstatic inline enum ntb_width ntb_link_sta_width(struct ntb_softc *);
367289648Scemstatic void atom_link_hb(void *arg);
368302484Smavstatic void ntb_link_event(device_t dev);
369302484Smavstatic void ntb_db_event(device_t dev, uint32_t vec);
370289648Scemstatic void recover_atom_link(void *arg);
371289546Scemstatic bool ntb_poll_link(struct ntb_softc *ntb);
372255274Scarlstatic void save_bar_parameters(struct ntb_pci_bar_info *bar);
373289774Scemstatic void ntb_sysctl_init(struct ntb_softc *);
374289774Scemstatic int sysctl_handle_features(SYSCTL_HANDLER_ARGS);
375300100Scemstatic int sysctl_handle_link_admin(SYSCTL_HANDLER_ARGS);
376300100Scemstatic int sysctl_handle_link_status_human(SYSCTL_HANDLER_ARGS);
377289774Scemstatic int sysctl_handle_link_status(SYSCTL_HANDLER_ARGS);
378289774Scemstatic int sysctl_handle_register(SYSCTL_HANDLER_ARGS);
379250079Scarl
380290685Scemstatic unsigned g_ntb_hw_debug_level;
381290685ScemSYSCTL_UINT(_hw_ntb, OID_AUTO, debug_level, CTLFLAG_RWTUN,
382290685Scem    &g_ntb_hw_debug_level, 0, "ntb_hw log level -- higher is more verbose");
383290685Scem#define ntb_printf(lvl, ...) do {				\
384290685Scem	if ((lvl) <= g_ntb_hw_debug_level) {			\
385290685Scem		device_printf(ntb->device, __VA_ARGS__);	\
386290685Scem	}							\
387290685Scem} while (0)
388290685Scem
389295486Scem#define	_NTB_PAT_UC	0
390295486Scem#define	_NTB_PAT_WC	1
391295486Scem#define	_NTB_PAT_WT	4
392295486Scem#define	_NTB_PAT_WP	5
393295486Scem#define	_NTB_PAT_WB	6
394295486Scem#define	_NTB_PAT_UCM	7
395295486Scemstatic unsigned g_ntb_mw_pat = _NTB_PAT_UC;
396295486ScemSYSCTL_UINT(_hw_ntb, OID_AUTO, default_mw_pat, CTLFLAG_RDTUN,
397295486Scem    &g_ntb_mw_pat, 0, "Configure the default memory window cache flags (PAT): "
398295486Scem    "UC: "  __XSTRING(_NTB_PAT_UC) ", "
399295486Scem    "WC: "  __XSTRING(_NTB_PAT_WC) ", "
400295486Scem    "WT: "  __XSTRING(_NTB_PAT_WT) ", "
401295486Scem    "WP: "  __XSTRING(_NTB_PAT_WP) ", "
402295486Scem    "WB: "  __XSTRING(_NTB_PAT_WB) ", "
403295486Scem    "UC-: " __XSTRING(_NTB_PAT_UCM));
404291030Scem
405295486Scemstatic inline vm_memattr_t
406295486Scemntb_pat_flags(void)
407295486Scem{
408295486Scem
409295486Scem	switch (g_ntb_mw_pat) {
410295486Scem	case _NTB_PAT_WC:
411295486Scem		return (VM_MEMATTR_WRITE_COMBINING);
412295486Scem	case _NTB_PAT_WT:
413295486Scem		return (VM_MEMATTR_WRITE_THROUGH);
414295486Scem	case _NTB_PAT_WP:
415295486Scem		return (VM_MEMATTR_WRITE_PROTECTED);
416295486Scem	case _NTB_PAT_WB:
417295486Scem		return (VM_MEMATTR_WRITE_BACK);
418295486Scem	case _NTB_PAT_UCM:
419295486Scem		return (VM_MEMATTR_WEAK_UNCACHEABLE);
420295486Scem	case _NTB_PAT_UC:
421295486Scem		/* FALLTHROUGH */
422295486Scem	default:
423295486Scem		return (VM_MEMATTR_UNCACHEABLE);
424295486Scem	}
425295486Scem}
426295486Scem
427295487Scem/*
428295487Scem * Well, this obviously doesn't belong here, but it doesn't seem to exist
429295487Scem * anywhere better yet.
430295487Scem */
431295487Scemstatic inline const char *
432295487Scemntb_vm_memattr_to_str(vm_memattr_t pat)
433295487Scem{
434295487Scem
435295487Scem	switch (pat) {
436295487Scem	case VM_MEMATTR_WRITE_COMBINING:
437295487Scem		return ("WRITE_COMBINING");
438295487Scem	case VM_MEMATTR_WRITE_THROUGH:
439295487Scem		return ("WRITE_THROUGH");
440295487Scem	case VM_MEMATTR_WRITE_PROTECTED:
441295487Scem		return ("WRITE_PROTECTED");
442295487Scem	case VM_MEMATTR_WRITE_BACK:
443295487Scem		return ("WRITE_BACK");
444295487Scem	case VM_MEMATTR_WEAK_UNCACHEABLE:
445295487Scem		return ("UNCACHED");
446295487Scem	case VM_MEMATTR_UNCACHEABLE:
447295487Scem		return ("UNCACHEABLE");
448295487Scem	default:
449295487Scem		return ("UNKNOWN");
450295487Scem	}
451295487Scem}
452295487Scem
453302508Smavstatic int g_ntb_msix_idx = 1;
454295618ScemSYSCTL_INT(_hw_ntb, OID_AUTO, msix_mw_idx, CTLFLAG_RDTUN, &g_ntb_msix_idx,
455295618Scem    0, "Use this memory window to access the peer MSIX message complex on "
456295618Scem    "certain Xeon-based NTB systems, as a workaround for a hardware errata.  "
457295618Scem    "Like b2b_mw_idx, negative values index from the last available memory "
458295618Scem    "window.  (Applies on Xeon platforms with SB01BASE_LOCKUP errata.)");
459295618Scem
460291263Scemstatic int g_ntb_mw_idx = -1;
461291263ScemSYSCTL_INT(_hw_ntb, OID_AUTO, b2b_mw_idx, CTLFLAG_RDTUN, &g_ntb_mw_idx,
462291263Scem    0, "Use this memory window to access the peer NTB registers.  A "
463291263Scem    "non-negative value starts from the first MW index; a negative value "
464291263Scem    "starts from the last MW index.  The default is -1, i.e., the last "
465291263Scem    "available memory window.  Both sides of the NTB MUST set the same "
466291263Scem    "value here!  (Applies on Xeon platforms with SDOORBELL_LOCKUP errata.)");
467291263Scem
468302484Smav/* Hardware owns the low 16 bits of features. */
469302484Smav#define NTB_BAR_SIZE_4K		(1 << 0)
470302484Smav#define NTB_SDOORBELL_LOCKUP	(1 << 1)
471302484Smav#define NTB_SB01BASE_LOCKUP	(1 << 2)
472302484Smav#define NTB_B2BDOORBELL_BIT14	(1 << 3)
473302484Smav/* Software/configuration owns the top 16 bits. */
474302484Smav#define NTB_SPLIT_BAR		(1ull << 16)
475302484Smav
476302484Smav#define NTB_FEATURES_STR \
477302484Smav    "\20\21SPLIT_BAR4\04B2B_DOORBELL_BIT14\03SB01BASE_LOCKUP" \
478302484Smav    "\02SDOORBELL_LOCKUP\01BAR_SIZE_4K"
479302484Smav
480250079Scarlstatic struct ntb_hw_info pci_ids[] = {
481289612Scem	/* XXX: PS/SS IDs left out until they are supported. */
482289612Scem	{ 0x0C4E8086, "BWD Atom Processor S1200 Non-Transparent Bridge B2B",
483289648Scem		NTB_ATOM, 0 },
484289233Scem
485289233Scem	{ 0x37258086, "JSF Xeon C35xx/C55xx Non-Transparent Bridge B2B",
486289538Scem		NTB_XEON, NTB_SDOORBELL_LOCKUP | NTB_B2BDOORBELL_BIT14 },
487289233Scem	{ 0x3C0D8086, "SNB Xeon E5/Core i7 Non-Transparent Bridge B2B",
488289538Scem		NTB_XEON, NTB_SDOORBELL_LOCKUP | NTB_B2BDOORBELL_BIT14 },
489289233Scem	{ 0x0E0D8086, "IVT Xeon E5 V2 Non-Transparent Bridge B2B", NTB_XEON,
490289538Scem		NTB_SDOORBELL_LOCKUP | NTB_B2BDOORBELL_BIT14 |
491289538Scem		    NTB_SB01BASE_LOCKUP | NTB_BAR_SIZE_4K },
492289233Scem	{ 0x2F0D8086, "HSX Xeon E5 V3 Non-Transparent Bridge B2B", NTB_XEON,
493289538Scem		NTB_SDOORBELL_LOCKUP | NTB_B2BDOORBELL_BIT14 |
494289538Scem		    NTB_SB01BASE_LOCKUP },
495289233Scem	{ 0x6F0D8086, "BDX Xeon E5 V4 Non-Transparent Bridge B2B", NTB_XEON,
496289538Scem		NTB_SDOORBELL_LOCKUP | NTB_B2BDOORBELL_BIT14 |
497289538Scem		    NTB_SB01BASE_LOCKUP },
498289233Scem
499289648Scem	{ 0x00000000, NULL, NTB_ATOM, 0 }
500250079Scarl};
501250079Scarl
502289648Scemstatic const struct ntb_reg atom_reg = {
503289648Scem	.ntb_ctl = ATOM_NTBCNTL_OFFSET,
504289648Scem	.lnk_sta = ATOM_LINK_STATUS_OFFSET,
505289542Scem	.db_size = sizeof(uint64_t),
506289542Scem	.mw_bar = { NTB_B2B_BAR_1, NTB_B2B_BAR_2 },
507289542Scem};
508289542Scem
509289648Scemstatic const struct ntb_alt_reg atom_pri_reg = {
510289648Scem	.db_bell = ATOM_PDOORBELL_OFFSET,
511289648Scem	.db_mask = ATOM_PDBMSK_OFFSET,
512289648Scem	.spad = ATOM_SPAD_OFFSET,
513289607Scem};
514289607Scem
515289648Scemstatic const struct ntb_alt_reg atom_b2b_reg = {
516289648Scem	.db_bell = ATOM_B2B_DOORBELL_OFFSET,
517289648Scem	.spad = ATOM_B2B_SPAD_OFFSET,
518289542Scem};
519289542Scem
520289648Scemstatic const struct ntb_xlat_reg atom_sec_xlat = {
521289542Scem#if 0
522289542Scem	/* "FIXME" says the Linux driver. */
523289648Scem	.bar0_base = ATOM_SBAR0BASE_OFFSET,
524289648Scem	.bar2_base = ATOM_SBAR2BASE_OFFSET,
525289648Scem	.bar4_base = ATOM_SBAR4BASE_OFFSET,
526289546Scem
527289648Scem	.bar2_limit = ATOM_SBAR2LMT_OFFSET,
528289648Scem	.bar4_limit = ATOM_SBAR4LMT_OFFSET,
529289542Scem#endif
530289546Scem
531289648Scem	.bar2_xlat = ATOM_SBAR2XLAT_OFFSET,
532289648Scem	.bar4_xlat = ATOM_SBAR4XLAT_OFFSET,
533289542Scem};
534289542Scem
535289542Scemstatic const struct ntb_reg xeon_reg = {
536289542Scem	.ntb_ctl = XEON_NTBCNTL_OFFSET,
537289542Scem	.lnk_sta = XEON_LINK_STATUS_OFFSET,
538289542Scem	.db_size = sizeof(uint16_t),
539289542Scem	.mw_bar = { NTB_B2B_BAR_1, NTB_B2B_BAR_2, NTB_B2B_BAR_3 },
540289542Scem};
541289542Scem
542289607Scemstatic const struct ntb_alt_reg xeon_pri_reg = {
543289607Scem	.db_bell = XEON_PDOORBELL_OFFSET,
544289607Scem	.db_mask = XEON_PDBMSK_OFFSET,
545289607Scem	.spad = XEON_SPAD_OFFSET,
546289607Scem};
547289607Scem
548289542Scemstatic const struct ntb_alt_reg xeon_b2b_reg = {
549289542Scem	.db_bell = XEON_B2B_DOORBELL_OFFSET,
550289542Scem	.spad = XEON_B2B_SPAD_OFFSET,
551289542Scem};
552289542Scem
553289542Scemstatic const struct ntb_xlat_reg xeon_sec_xlat = {
554289542Scem	.bar0_base = XEON_SBAR0BASE_OFFSET,
555289546Scem	.bar2_base = XEON_SBAR2BASE_OFFSET,
556289546Scem	.bar4_base = XEON_SBAR4BASE_OFFSET,
557289546Scem	.bar5_base = XEON_SBAR5BASE_OFFSET,
558289546Scem
559289542Scem	.bar2_limit = XEON_SBAR2LMT_OFFSET,
560289546Scem	.bar4_limit = XEON_SBAR4LMT_OFFSET,
561289546Scem	.bar5_limit = XEON_SBAR5LMT_OFFSET,
562289546Scem
563289542Scem	.bar2_xlat = XEON_SBAR2XLAT_OFFSET,
564289546Scem	.bar4_xlat = XEON_SBAR4XLAT_OFFSET,
565289546Scem	.bar5_xlat = XEON_SBAR5XLAT_OFFSET,
566289542Scem};
567289542Scem
568289614Scemstatic struct ntb_b2b_addr xeon_b2b_usd_addr = {
569290725Scem	.bar0_addr = XEON_B2B_BAR0_ADDR,
570290725Scem	.bar2_addr64 = XEON_B2B_BAR2_ADDR64,
571290725Scem	.bar4_addr64 = XEON_B2B_BAR4_ADDR64,
572290725Scem	.bar4_addr32 = XEON_B2B_BAR4_ADDR32,
573290725Scem	.bar5_addr32 = XEON_B2B_BAR5_ADDR32,
574289542Scem};
575289542Scem
576289614Scemstatic struct ntb_b2b_addr xeon_b2b_dsd_addr = {
577290725Scem	.bar0_addr = XEON_B2B_BAR0_ADDR,
578290725Scem	.bar2_addr64 = XEON_B2B_BAR2_ADDR64,
579290725Scem	.bar4_addr64 = XEON_B2B_BAR4_ADDR64,
580290725Scem	.bar4_addr32 = XEON_B2B_BAR4_ADDR32,
581290725Scem	.bar5_addr32 = XEON_B2B_BAR5_ADDR32,
582289542Scem};
583289542Scem
584289614ScemSYSCTL_NODE(_hw_ntb, OID_AUTO, xeon_b2b, CTLFLAG_RW, 0,
585289614Scem    "B2B MW segment overrides -- MUST be the same on both sides");
586289614Scem
587289614ScemSYSCTL_UQUAD(_hw_ntb_xeon_b2b, OID_AUTO, usd_bar2_addr64, CTLFLAG_RDTUN,
588289614Scem    &xeon_b2b_usd_addr.bar2_addr64, 0, "If using B2B topology on Xeon "
589289614Scem    "hardware, use this 64-bit address on the bus between the NTB devices for "
590289614Scem    "the window at BAR2, on the upstream side of the link.  MUST be the same "
591289614Scem    "address on both sides.");
592289614ScemSYSCTL_UQUAD(_hw_ntb_xeon_b2b, OID_AUTO, usd_bar4_addr64, CTLFLAG_RDTUN,
593289614Scem    &xeon_b2b_usd_addr.bar4_addr64, 0, "See usd_bar2_addr64, but BAR4.");
594289614ScemSYSCTL_UQUAD(_hw_ntb_xeon_b2b, OID_AUTO, usd_bar4_addr32, CTLFLAG_RDTUN,
595289614Scem    &xeon_b2b_usd_addr.bar4_addr32, 0, "See usd_bar2_addr64, but BAR4 "
596289614Scem    "(split-BAR mode).");
597289614ScemSYSCTL_UQUAD(_hw_ntb_xeon_b2b, OID_AUTO, usd_bar5_addr32, CTLFLAG_RDTUN,
598289646Scem    &xeon_b2b_usd_addr.bar5_addr32, 0, "See usd_bar2_addr64, but BAR5 "
599289614Scem    "(split-BAR mode).");
600289614Scem
601289614ScemSYSCTL_UQUAD(_hw_ntb_xeon_b2b, OID_AUTO, dsd_bar2_addr64, CTLFLAG_RDTUN,
602289614Scem    &xeon_b2b_dsd_addr.bar2_addr64, 0, "If using B2B topology on Xeon "
603289614Scem    "hardware, use this 64-bit address on the bus between the NTB devices for "
604289614Scem    "the window at BAR2, on the downstream side of the link.  MUST be the same"
605289614Scem    " address on both sides.");
606289614ScemSYSCTL_UQUAD(_hw_ntb_xeon_b2b, OID_AUTO, dsd_bar4_addr64, CTLFLAG_RDTUN,
607289614Scem    &xeon_b2b_dsd_addr.bar4_addr64, 0, "See dsd_bar2_addr64, but BAR4.");
608289614ScemSYSCTL_UQUAD(_hw_ntb_xeon_b2b, OID_AUTO, dsd_bar4_addr32, CTLFLAG_RDTUN,
609289614Scem    &xeon_b2b_dsd_addr.bar4_addr32, 0, "See dsd_bar2_addr64, but BAR4 "
610289614Scem    "(split-BAR mode).");
611289614ScemSYSCTL_UQUAD(_hw_ntb_xeon_b2b, OID_AUTO, dsd_bar5_addr32, CTLFLAG_RDTUN,
612289646Scem    &xeon_b2b_dsd_addr.bar5_addr32, 0, "See dsd_bar2_addr64, but BAR5 "
613289614Scem    "(split-BAR mode).");
614289614Scem
615250079Scarl/*
616250079Scarl * OS <-> Driver interface structures
617250079Scarl */
618250079ScarlMALLOC_DEFINE(M_NTB, "ntb_hw", "ntb_hw driver memory allocations");
619250079Scarl
620289207ScemSYSCTL_NODE(_hw, OID_AUTO, ntb, CTLFLAG_RW, 0, "NTB sysctls");
621289207Scem
622250079Scarl/*
623250079Scarl * OS <-> Driver linkage functions
624250079Scarl */
625250079Scarlstatic int
626250079Scarlntb_probe(device_t device)
627250079Scarl{
628289209Scem	struct ntb_hw_info *p;
629250079Scarl
630289209Scem	p = ntb_get_device_info(pci_get_devid(device));
631289209Scem	if (p == NULL)
632250079Scarl		return (ENXIO);
633289209Scem
634289209Scem	device_set_desc(device, p->desc);
635289209Scem	return (0);
636250079Scarl}
637250079Scarl
638250079Scarlstatic int
639250079Scarlntb_attach(device_t device)
640250079Scarl{
641289209Scem	struct ntb_softc *ntb;
642289209Scem	struct ntb_hw_info *p;
643250079Scarl	int error;
644250079Scarl
645302484Smav	ntb = device_get_softc(device);
646289209Scem	p = ntb_get_device_info(pci_get_devid(device));
647289209Scem
648250079Scarl	ntb->device = device;
649250079Scarl	ntb->type = p->type;
650255274Scarl	ntb->features = p->features;
651289543Scem	ntb->b2b_mw_idx = B2B_MW_DISABLED;
652295618Scem	ntb->msix_mw_idx = B2B_MW_DISABLED;
653250079Scarl
654289648Scem	/* Heartbeat timer for NTB_ATOM since there is no link interrupt */
655283291Sjkim	callout_init(&ntb->heartbeat_timer, 1);
656283291Sjkim	callout_init(&ntb->lr_timer, 1);
657295618Scem	callout_init(&ntb->peer_msix_work, 1);
658289542Scem	mtx_init(&ntb->db_mask_lock, "ntb hw bits", NULL, MTX_SPIN);
659302491Smav	rm_init(&ntb->ctx_lock, "ntb ctx");
660250079Scarl
661289648Scem	if (ntb->type == NTB_ATOM)
662289648Scem		error = ntb_detect_atom(ntb);
663289348Scem	else
664289348Scem		error = ntb_detect_xeon(ntb);
665290682Scem	if (error != 0)
666289348Scem		goto out;
667289348Scem
668289397Scem	ntb_detect_max_mw(ntb);
669289396Scem
670290682Scem	pci_enable_busmaster(ntb->device);
671290682Scem
672289209Scem	error = ntb_map_pci_bars(ntb);
673290682Scem	if (error != 0)
674289209Scem		goto out;
675289648Scem	if (ntb->type == NTB_ATOM)
676289648Scem		error = ntb_atom_init_dev(ntb);
677289272Scem	else
678289542Scem		error = ntb_xeon_init_dev(ntb);
679290682Scem	if (error != 0)
680289209Scem		goto out;
681290682Scem
682302484Smav	ntb_spad_clear(device);
683295618Scem
684290682Scem	ntb_poll_link(ntb);
685290682Scem
686289774Scem	ntb_sysctl_init(ntb);
687250079Scarl
688302484Smav	/* Attach children to this controller */
689302484Smav	device_add_child(device, NULL, -1);
690302484Smav	bus_generic_attach(device);
691302484Smav
692289209Scemout:
693289209Scem	if (error != 0)
694289209Scem		ntb_detach(device);
695250079Scarl	return (error);
696250079Scarl}
697250079Scarl
698250079Scarlstatic int
699250079Scarlntb_detach(device_t device)
700250079Scarl{
701289209Scem	struct ntb_softc *ntb;
702250079Scarl
703302484Smav	ntb = device_get_softc(device);
704289542Scem
705302484Smav	/* Detach & delete all children */
706302484Smav	device_delete_children(device);
707302484Smav
708295618Scem	if (ntb->self_reg != NULL) {
709295618Scem		DB_MASK_LOCK(ntb);
710295618Scem		db_iowrite(ntb, ntb->self_reg->db_mask, ntb->db_valid_mask);
711295618Scem		DB_MASK_UNLOCK(ntb);
712295618Scem	}
713250079Scarl	callout_drain(&ntb->heartbeat_timer);
714250079Scarl	callout_drain(&ntb->lr_timer);
715295618Scem	callout_drain(&ntb->peer_msix_work);
716290682Scem	pci_disable_busmaster(ntb->device);
717289272Scem	if (ntb->type == NTB_XEON)
718289272Scem		ntb_teardown_xeon(ntb);
719250079Scarl	ntb_teardown_interrupts(ntb);
720289397Scem
721289542Scem	mtx_destroy(&ntb->db_mask_lock);
722302491Smav	rm_destroy(&ntb->ctx_lock);
723289542Scem
724250079Scarl	ntb_unmap_pci_bar(ntb);
725250079Scarl
726250079Scarl	return (0);
727250079Scarl}
728250079Scarl
729289542Scem/*
730289542Scem * Driver internal routines
731289542Scem */
732289539Scemstatic inline enum ntb_bar
733289539Scemntb_mw_to_bar(struct ntb_softc *ntb, unsigned mw)
734289539Scem{
735289539Scem
736291263Scem	KASSERT(mw < ntb->mw_count,
737289542Scem	    ("%s: mw:%u > count:%u", __func__, mw, (unsigned)ntb->mw_count));
738289546Scem	KASSERT(ntb->reg->mw_bar[mw] != 0, ("invalid mw"));
739289539Scem
740289542Scem	return (ntb->reg->mw_bar[mw]);
741289539Scem}
742289539Scem
743289546Scemstatic inline bool
744289546Scembar_is_64bit(struct ntb_softc *ntb, enum ntb_bar bar)
745289546Scem{
746289546Scem	/* XXX This assertion could be stronger. */
747289546Scem	KASSERT(bar < NTB_MAX_BARS, ("bogus bar"));
748302484Smav	return (bar < NTB_B2B_BAR_2 || !HAS_FEATURE(ntb, NTB_SPLIT_BAR));
749289546Scem}
750289546Scem
751289546Scemstatic inline void
752289546Scembar_get_xlat_params(struct ntb_softc *ntb, enum ntb_bar bar, uint32_t *base,
753289546Scem    uint32_t *xlat, uint32_t *lmt)
754289546Scem{
755289546Scem	uint32_t basev, lmtv, xlatv;
756289546Scem
757289546Scem	switch (bar) {
758289546Scem	case NTB_B2B_BAR_1:
759289546Scem		basev = ntb->xlat_reg->bar2_base;
760289546Scem		lmtv = ntb->xlat_reg->bar2_limit;
761289546Scem		xlatv = ntb->xlat_reg->bar2_xlat;
762289546Scem		break;
763289546Scem	case NTB_B2B_BAR_2:
764289546Scem		basev = ntb->xlat_reg->bar4_base;
765289546Scem		lmtv = ntb->xlat_reg->bar4_limit;
766289546Scem		xlatv = ntb->xlat_reg->bar4_xlat;
767289546Scem		break;
768289546Scem	case NTB_B2B_BAR_3:
769289546Scem		basev = ntb->xlat_reg->bar5_base;
770289546Scem		lmtv = ntb->xlat_reg->bar5_limit;
771289546Scem		xlatv = ntb->xlat_reg->bar5_xlat;
772289546Scem		break;
773289546Scem	default:
774289546Scem		KASSERT(bar >= NTB_B2B_BAR_1 && bar < NTB_MAX_BARS,
775289546Scem		    ("bad bar"));
776289546Scem		basev = lmtv = xlatv = 0;
777289546Scem		break;
778289546Scem	}
779289546Scem
780289546Scem	if (base != NULL)
781289546Scem		*base = basev;
782289546Scem	if (xlat != NULL)
783289546Scem		*xlat = xlatv;
784289546Scem	if (lmt != NULL)
785289546Scem		*lmt = lmtv;
786289546Scem}
787289546Scem
788250079Scarlstatic int
789255272Scarlntb_map_pci_bars(struct ntb_softc *ntb)
790250079Scarl{
791255272Scarl	int rc;
792250079Scarl
793250079Scarl	ntb->bar_info[NTB_CONFIG_BAR].pci_resource_id = PCIR_BAR(0);
794289541Scem	rc = map_mmr_bar(ntb, &ntb->bar_info[NTB_CONFIG_BAR]);
795255272Scarl	if (rc != 0)
796289541Scem		goto out;
797255272Scarl
798289209Scem	ntb->bar_info[NTB_B2B_BAR_1].pci_resource_id = PCIR_BAR(2);
799289541Scem	rc = map_memory_window_bar(ntb, &ntb->bar_info[NTB_B2B_BAR_1]);
800255272Scarl	if (rc != 0)
801289541Scem		goto out;
802289543Scem	ntb->bar_info[NTB_B2B_BAR_1].psz_off = XEON_PBAR23SZ_OFFSET;
803289543Scem	ntb->bar_info[NTB_B2B_BAR_1].ssz_off = XEON_SBAR23SZ_OFFSET;
804289543Scem	ntb->bar_info[NTB_B2B_BAR_1].pbarxlat_off = XEON_PBAR2XLAT_OFFSET;
805255272Scarl
806289209Scem	ntb->bar_info[NTB_B2B_BAR_2].pci_resource_id = PCIR_BAR(4);
807291263Scem	rc = map_memory_window_bar(ntb, &ntb->bar_info[NTB_B2B_BAR_2]);
808291263Scem	if (rc != 0)
809291263Scem		goto out;
810289543Scem	ntb->bar_info[NTB_B2B_BAR_2].psz_off = XEON_PBAR4SZ_OFFSET;
811289543Scem	ntb->bar_info[NTB_B2B_BAR_2].ssz_off = XEON_SBAR4SZ_OFFSET;
812289543Scem	ntb->bar_info[NTB_B2B_BAR_2].pbarxlat_off = XEON_PBAR4XLAT_OFFSET;
813289543Scem
814302484Smav	if (!HAS_FEATURE(ntb, NTB_SPLIT_BAR))
815289541Scem		goto out;
816289397Scem
817289397Scem	ntb->bar_info[NTB_B2B_BAR_3].pci_resource_id = PCIR_BAR(5);
818291263Scem	rc = map_memory_window_bar(ntb, &ntb->bar_info[NTB_B2B_BAR_3]);
819289543Scem	ntb->bar_info[NTB_B2B_BAR_3].psz_off = XEON_PBAR5SZ_OFFSET;
820289543Scem	ntb->bar_info[NTB_B2B_BAR_3].ssz_off = XEON_SBAR5SZ_OFFSET;
821289543Scem	ntb->bar_info[NTB_B2B_BAR_3].pbarxlat_off = XEON_PBAR5XLAT_OFFSET;
822250079Scarl
823289541Scemout:
824289209Scem	if (rc != 0)
825255272Scarl		device_printf(ntb->device,
826255272Scarl		    "unable to allocate pci resource\n");
827255272Scarl	return (rc);
828255272Scarl}
829255272Scarl
830289541Scemstatic void
831289647Scemprint_map_success(struct ntb_softc *ntb, struct ntb_pci_bar_info *bar,
832289647Scem    const char *kind)
833289541Scem{
834289541Scem
835289647Scem	device_printf(ntb->device,
836289647Scem	    "Mapped BAR%d v:[%p-%p] p:[%p-%p] (0x%jx bytes) (%s)\n",
837289647Scem	    PCI_RID2BAR(bar->pci_resource_id), bar->vbase,
838289647Scem	    (char *)bar->vbase + bar->size - 1,
839289647Scem	    (void *)bar->pbase, (void *)(bar->pbase + bar->size - 1),
840289647Scem	    (uintmax_t)bar->size, kind);
841289541Scem}
842289541Scem
843255272Scarlstatic int
844255272Scarlmap_mmr_bar(struct ntb_softc *ntb, struct ntb_pci_bar_info *bar)
845255272Scarl{
846255272Scarl
847255275Scarl	bar->pci_resource = bus_alloc_resource_any(ntb->device, SYS_RES_MEMORY,
848289209Scem	    &bar->pci_resource_id, RF_ACTIVE);
849255272Scarl	if (bar->pci_resource == NULL)
850255272Scarl		return (ENXIO);
851289209Scem
852289209Scem	save_bar_parameters(bar);
853291280Scem	bar->map_mode = VM_MEMATTR_UNCACHEABLE;
854289647Scem	print_map_success(ntb, bar, "mmr");
855289209Scem	return (0);
856255272Scarl}
857255272Scarl
858255272Scarlstatic int
859255272Scarlmap_memory_window_bar(struct ntb_softc *ntb, struct ntb_pci_bar_info *bar)
860255272Scarl{
861255272Scarl	int rc;
862291280Scem	vm_memattr_t mapmode;
863255276Scarl	uint8_t bar_size_bits = 0;
864255272Scarl
865289209Scem	bar->pci_resource = bus_alloc_resource_any(ntb->device, SYS_RES_MEMORY,
866289209Scem	    &bar->pci_resource_id, RF_ACTIVE);
867250079Scarl
868255272Scarl	if (bar->pci_resource == NULL)
869255272Scarl		return (ENXIO);
870255276Scarl
871289209Scem	save_bar_parameters(bar);
872289209Scem	/*
873289209Scem	 * Ivytown NTB BAR sizes are misreported by the hardware due to a
874289209Scem	 * hardware issue. To work around this, query the size it should be
875289209Scem	 * configured to by the device and modify the resource to correspond to
876289209Scem	 * this new size. The BIOS on systems with this problem is required to
877289209Scem	 * provide enough address space to allow the driver to make this change
878289209Scem	 * safely.
879289209Scem	 *
880289209Scem	 * Ideally I could have just specified the size when I allocated the
881289209Scem	 * resource like:
882289209Scem	 *  bus_alloc_resource(ntb->device,
883289209Scem	 *	SYS_RES_MEMORY, &bar->pci_resource_id, 0ul, ~0ul,
884289209Scem	 *	1ul << bar_size_bits, RF_ACTIVE);
885289209Scem	 * but the PCI driver does not honor the size in this call, so we have
886289209Scem	 * to modify it after the fact.
887289209Scem	 */
888302484Smav	if (HAS_FEATURE(ntb, NTB_BAR_SIZE_4K)) {
889289209Scem		if (bar->pci_resource_id == PCIR_BAR(2))
890289209Scem			bar_size_bits = pci_read_config(ntb->device,
891289209Scem			    XEON_PBAR23SZ_OFFSET, 1);
892289209Scem		else
893289209Scem			bar_size_bits = pci_read_config(ntb->device,
894289209Scem			    XEON_PBAR45SZ_OFFSET, 1);
895289209Scem
896289209Scem		rc = bus_adjust_resource(ntb->device, SYS_RES_MEMORY,
897289209Scem		    bar->pci_resource, bar->pbase,
898289209Scem		    bar->pbase + (1ul << bar_size_bits) - 1);
899255272Scarl		if (rc != 0) {
900289209Scem			device_printf(ntb->device,
901289209Scem			    "unable to resize bar\n");
902255272Scarl			return (rc);
903250079Scarl		}
904289209Scem
905289209Scem		save_bar_parameters(bar);
906250079Scarl	}
907289209Scem
908291280Scem	bar->map_mode = VM_MEMATTR_UNCACHEABLE;
909291030Scem	print_map_success(ntb, bar, "mw");
910291280Scem
911295486Scem	/*
912295486Scem	 * Optionally, mark MW BARs as anything other than UC to improve
913295486Scem	 * performance.
914295486Scem	 */
915295486Scem	mapmode = ntb_pat_flags();
916295486Scem	if (mapmode == bar->map_mode)
917295486Scem		return (0);
918291030Scem
919291280Scem	rc = pmap_change_attr((vm_offset_t)bar->vbase, bar->size, mapmode);
920291031Scem	if (rc == 0) {
921291280Scem		bar->map_mode = mapmode;
922289209Scem		device_printf(ntb->device,
923289647Scem		    "Marked BAR%d v:[%p-%p] p:[%p-%p] as "
924291280Scem		    "%s.\n",
925289647Scem		    PCI_RID2BAR(bar->pci_resource_id), bar->vbase,
926289647Scem		    (char *)bar->vbase + bar->size - 1,
927291280Scem		    (void *)bar->pbase, (void *)(bar->pbase + bar->size - 1),
928295487Scem		    ntb_vm_memattr_to_str(mapmode));
929291031Scem	} else
930289647Scem		device_printf(ntb->device,
931289647Scem		    "Unable to mark BAR%d v:[%p-%p] p:[%p-%p] as "
932291280Scem		    "%s: %d\n",
933289647Scem		    PCI_RID2BAR(bar->pci_resource_id), bar->vbase,
934289647Scem		    (char *)bar->vbase + bar->size - 1,
935289647Scem		    (void *)bar->pbase, (void *)(bar->pbase + bar->size - 1),
936295487Scem		    ntb_vm_memattr_to_str(mapmode), rc);
937289647Scem		/* Proceed anyway */
938250079Scarl	return (0);
939250079Scarl}
940250079Scarl
941250079Scarlstatic void
942250079Scarlntb_unmap_pci_bar(struct ntb_softc *ntb)
943250079Scarl{
944250079Scarl	struct ntb_pci_bar_info *current_bar;
945250079Scarl	int i;
946250079Scarl
947289397Scem	for (i = 0; i < NTB_MAX_BARS; i++) {
948250079Scarl		current_bar = &ntb->bar_info[i];
949250079Scarl		if (current_bar->pci_resource != NULL)
950250079Scarl			bus_release_resource(ntb->device, SYS_RES_MEMORY,
951250079Scarl			    current_bar->pci_resource_id,
952250079Scarl			    current_bar->pci_resource);
953250079Scarl	}
954250079Scarl}
955250079Scarl
956250079Scarlstatic int
957289540Scemntb_setup_msix(struct ntb_softc *ntb, uint32_t num_vectors)
958250079Scarl{
959289342Scem	uint32_t i;
960289342Scem	int rc;
961289342Scem
962289342Scem	for (i = 0; i < num_vectors; i++) {
963289342Scem		ntb->int_info[i].rid = i + 1;
964289342Scem		ntb->int_info[i].res = bus_alloc_resource_any(ntb->device,
965289342Scem		    SYS_RES_IRQ, &ntb->int_info[i].rid, RF_ACTIVE);
966289342Scem		if (ntb->int_info[i].res == NULL) {
967289342Scem			device_printf(ntb->device,
968289342Scem			    "bus_alloc_resource failed\n");
969289342Scem			return (ENOMEM);
970289342Scem		}
971289342Scem		ntb->int_info[i].tag = NULL;
972289342Scem		ntb->allocated_interrupts++;
973289342Scem		rc = bus_setup_intr(ntb->device, ntb->int_info[i].res,
974289546Scem		    INTR_MPSAFE | INTR_TYPE_MISC, NULL, ndev_vec_isr,
975289546Scem		    &ntb->msix_vec[i], &ntb->int_info[i].tag);
976289342Scem		if (rc != 0) {
977289342Scem			device_printf(ntb->device, "bus_setup_intr failed\n");
978289342Scem			return (ENXIO);
979289342Scem		}
980289342Scem	}
981289342Scem	return (0);
982289342Scem}
983289342Scem
984289344Scem/*
985289344Scem * The Linux NTB driver drops from MSI-X to legacy INTx if a unique vector
986289344Scem * cannot be allocated for each MSI-X message.  JHB seems to think remapping
987289344Scem * should be okay.  This tunable should enable us to test that hypothesis
988289344Scem * when someone gets their hands on some Xeon hardware.
989289344Scem */
990289344Scemstatic int ntb_force_remap_mode;
991289344ScemSYSCTL_INT(_hw_ntb, OID_AUTO, force_remap_mode, CTLFLAG_RDTUN,
992289344Scem    &ntb_force_remap_mode, 0, "If enabled, force MSI-X messages to be remapped"
993289344Scem    " to a smaller number of ithreads, even if the desired number are "
994289344Scem    "available");
995289344Scem
996289344Scem/*
997289344Scem * In case it is NOT ok, give consumers an abort button.
998289344Scem */
999289344Scemstatic int ntb_prefer_intx;
1000289344ScemSYSCTL_INT(_hw_ntb, OID_AUTO, prefer_intx_to_remap, CTLFLAG_RDTUN,
1001289344Scem    &ntb_prefer_intx, 0, "If enabled, prefer to use legacy INTx mode rather "
1002289344Scem    "than remapping MSI-X messages over available slots (match Linux driver "
1003289344Scem    "behavior)");
1004289344Scem
1005289344Scem/*
1006289344Scem * Remap the desired number of MSI-X messages to available ithreads in a simple
1007289344Scem * round-robin fashion.
1008289344Scem */
1009289342Scemstatic int
1010289344Scemntb_remap_msix(device_t dev, uint32_t desired, uint32_t avail)
1011289344Scem{
1012289344Scem	u_int *vectors;
1013289344Scem	uint32_t i;
1014289344Scem	int rc;
1015289344Scem
1016289344Scem	if (ntb_prefer_intx != 0)
1017289344Scem		return (ENXIO);
1018289344Scem
1019289344Scem	vectors = malloc(desired * sizeof(*vectors), M_NTB, M_ZERO | M_WAITOK);
1020289344Scem
1021289344Scem	for (i = 0; i < desired; i++)
1022289344Scem		vectors[i] = (i % avail) + 1;
1023289344Scem
1024289344Scem	rc = pci_remap_msix(dev, desired, vectors);
1025289344Scem	free(vectors, M_NTB);
1026289344Scem	return (rc);
1027289344Scem}
1028289344Scem
1029289344Scemstatic int
1030289540Scemntb_init_isr(struct ntb_softc *ntb)
1031289342Scem{
1032289344Scem	uint32_t desired_vectors, num_vectors;
1033289342Scem	int rc;
1034250079Scarl
1035250079Scarl	ntb->allocated_interrupts = 0;
1036289542Scem	ntb->last_ts = ticks;
1037289347Scem
1038250079Scarl	/*
1039295618Scem	 * Mask all doorbell interrupts.  (Except link events!)
1040250079Scarl	 */
1041295618Scem	DB_MASK_LOCK(ntb);
1042295618Scem	ntb->db_mask = ntb->db_valid_mask;
1043295618Scem	db_iowrite(ntb, ntb->self_reg->db_mask, ntb->db_mask);
1044295618Scem	DB_MASK_UNLOCK(ntb);
1045250079Scarl
1046289344Scem	num_vectors = desired_vectors = MIN(pci_msix_count(ntb->device),
1047289539Scem	    ntb->db_count);
1048289344Scem	if (desired_vectors >= 1) {
1049289344Scem		rc = pci_alloc_msix(ntb->device, &num_vectors);
1050250079Scarl
1051289344Scem		if (ntb_force_remap_mode != 0 && rc == 0 &&
1052289344Scem		    num_vectors == desired_vectors)
1053289344Scem			num_vectors--;
1054289344Scem
1055289344Scem		if (rc == 0 && num_vectors < desired_vectors) {
1056289344Scem			rc = ntb_remap_msix(ntb->device, desired_vectors,
1057289344Scem			    num_vectors);
1058289344Scem			if (rc == 0)
1059289344Scem				num_vectors = desired_vectors;
1060289344Scem			else
1061289344Scem				pci_release_msi(ntb->device);
1062289344Scem		}
1063289344Scem		if (rc != 0)
1064289344Scem			num_vectors = 1;
1065289344Scem	} else
1066289344Scem		num_vectors = 1;
1067289344Scem
1068289539Scem	if (ntb->type == NTB_XEON && num_vectors < ntb->db_vec_count) {
1069302484Smav		if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP)) {
1070295618Scem			device_printf(ntb->device,
1071295618Scem			    "Errata workaround does not support MSI or INTX\n");
1072295618Scem			return (EINVAL);
1073295618Scem		}
1074295618Scem
1075289539Scem		ntb->db_vec_count = 1;
1076290680Scem		ntb->db_vec_shift = XEON_DB_TOTAL_SHIFT;
1077289539Scem		rc = ntb_setup_legacy_interrupt(ntb);
1078289539Scem	} else {
1079300531Scem		if (num_vectors - 1 != XEON_NONLINK_DB_MSIX_BITS &&
1080302484Smav		    HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP)) {
1081300531Scem			device_printf(ntb->device,
1082300531Scem			    "Errata workaround expects %d doorbell bits\n",
1083300531Scem			    XEON_NONLINK_DB_MSIX_BITS);
1084300531Scem			return (EINVAL);
1085300531Scem		}
1086300531Scem
1087289546Scem		ntb_create_msix_vec(ntb, num_vectors);
1088289540Scem		rc = ntb_setup_msix(ntb, num_vectors);
1089302484Smav		if (rc == 0 && HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP))
1090300531Scem			ntb_get_msix_info(ntb);
1091289539Scem	}
1092289539Scem	if (rc != 0) {
1093289539Scem		device_printf(ntb->device,
1094289539Scem		    "Error allocating interrupts: %d\n", rc);
1095289546Scem		ntb_free_msix_vec(ntb);
1096289396Scem	}
1097289396Scem
1098289342Scem	return (rc);
1099289342Scem}
1100289342Scem
1101289342Scemstatic int
1102289342Scemntb_setup_legacy_interrupt(struct ntb_softc *ntb)
1103289342Scem{
1104289342Scem	int rc;
1105289342Scem
1106289342Scem	ntb->int_info[0].rid = 0;
1107289342Scem	ntb->int_info[0].res = bus_alloc_resource_any(ntb->device, SYS_RES_IRQ,
1108289342Scem	    &ntb->int_info[0].rid, RF_SHAREABLE|RF_ACTIVE);
1109289342Scem	if (ntb->int_info[0].res == NULL) {
1110289342Scem		device_printf(ntb->device, "bus_alloc_resource failed\n");
1111289342Scem		return (ENOMEM);
1112250079Scarl	}
1113250079Scarl
1114289342Scem	ntb->int_info[0].tag = NULL;
1115289342Scem	ntb->allocated_interrupts = 1;
1116289342Scem
1117289342Scem	rc = bus_setup_intr(ntb->device, ntb->int_info[0].res,
1118289546Scem	    INTR_MPSAFE | INTR_TYPE_MISC, NULL, ndev_irq_isr,
1119289342Scem	    ntb, &ntb->int_info[0].tag);
1120289342Scem	if (rc != 0) {
1121289342Scem		device_printf(ntb->device, "bus_setup_intr failed\n");
1122289342Scem		return (ENXIO);
1123289342Scem	}
1124289342Scem
1125250079Scarl	return (0);
1126250079Scarl}
1127250079Scarl
1128250079Scarlstatic void
1129250079Scarlntb_teardown_interrupts(struct ntb_softc *ntb)
1130250079Scarl{
1131250079Scarl	struct ntb_int_info *current_int;
1132250079Scarl	int i;
1133250079Scarl
1134289209Scem	for (i = 0; i < ntb->allocated_interrupts; i++) {
1135250079Scarl		current_int = &ntb->int_info[i];
1136250079Scarl		if (current_int->tag != NULL)
1137250079Scarl			bus_teardown_intr(ntb->device, current_int->res,
1138250079Scarl			    current_int->tag);
1139250079Scarl
1140250079Scarl		if (current_int->res != NULL)
1141250079Scarl			bus_release_resource(ntb->device, SYS_RES_IRQ,
1142250079Scarl			    rman_get_rid(current_int->res), current_int->res);
1143250079Scarl	}
1144250079Scarl
1145289546Scem	ntb_free_msix_vec(ntb);
1146250079Scarl	pci_release_msi(ntb->device);
1147250079Scarl}
1148250079Scarl
1149289347Scem/*
1150289648Scem * Doorbell register and mask are 64-bit on Atom, 16-bit on Xeon.  Abstract it
1151289347Scem * out to make code clearer.
1152289347Scem */
1153289539Scemstatic inline uint64_t
1154289546Scemdb_ioread(struct ntb_softc *ntb, uint64_t regoff)
1155289347Scem{
1156289347Scem
1157289648Scem	if (ntb->type == NTB_ATOM)
1158289347Scem		return (ntb_reg_read(8, regoff));
1159289347Scem
1160289347Scem	KASSERT(ntb->type == NTB_XEON, ("bad ntb type"));
1161289347Scem
1162289347Scem	return (ntb_reg_read(2, regoff));
1163289347Scem}
1164289347Scem
1165289539Scemstatic inline void
1166289546Scemdb_iowrite(struct ntb_softc *ntb, uint64_t regoff, uint64_t val)
1167289347Scem{
1168289347Scem
1169289542Scem	KASSERT((val & ~ntb->db_valid_mask) == 0,
1170289542Scem	    ("%s: Invalid bits 0x%jx (valid: 0x%jx)", __func__,
1171289542Scem	     (uintmax_t)(val & ~ntb->db_valid_mask),
1172289542Scem	     (uintmax_t)ntb->db_valid_mask));
1173289542Scem
1174289607Scem	if (regoff == ntb->self_reg->db_mask)
1175289546Scem		DB_MASK_ASSERT(ntb, MA_OWNED);
1176290678Scem	db_iowrite_raw(ntb, regoff, val);
1177290678Scem}
1178289542Scem
1179290678Scemstatic inline void
1180290678Scemdb_iowrite_raw(struct ntb_softc *ntb, uint64_t regoff, uint64_t val)
1181290678Scem{
1182290678Scem
1183289648Scem	if (ntb->type == NTB_ATOM) {
1184289347Scem		ntb_reg_write(8, regoff, val);
1185289347Scem		return;
1186289347Scem	}
1187289347Scem
1188289347Scem	KASSERT(ntb->type == NTB_XEON, ("bad ntb type"));
1189289347Scem	ntb_reg_write(2, regoff, (uint16_t)val);
1190289347Scem}
1191289347Scem
1192302484Smavstatic void
1193302484Smavntb_db_set_mask(device_t dev, uint64_t bits)
1194289542Scem{
1195302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
1196289542Scem
1197289546Scem	DB_MASK_LOCK(ntb);
1198289542Scem	ntb->db_mask |= bits;
1199302493Smav	if (!HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP))
1200302493Smav		db_iowrite(ntb, ntb->self_reg->db_mask, ntb->db_mask);
1201289546Scem	DB_MASK_UNLOCK(ntb);
1202289542Scem}
1203289542Scem
1204302484Smavstatic void
1205302484Smavntb_db_clear_mask(device_t dev, uint64_t bits)
1206289542Scem{
1207302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
1208302493Smav	uint64_t ibits;
1209302493Smav	int i;
1210289542Scem
1211289542Scem	KASSERT((bits & ~ntb->db_valid_mask) == 0,
1212289542Scem	    ("%s: Invalid bits 0x%jx (valid: 0x%jx)", __func__,
1213289542Scem	     (uintmax_t)(bits & ~ntb->db_valid_mask),
1214289542Scem	     (uintmax_t)ntb->db_valid_mask));
1215289542Scem
1216289546Scem	DB_MASK_LOCK(ntb);
1217302493Smav	ibits = ntb->fake_db_bell & ntb->db_mask & bits;
1218289542Scem	ntb->db_mask &= ~bits;
1219302493Smav	if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP)) {
1220302493Smav		/* Simulate fake interrupts if unmasked DB bits are set. */
1221302493Smav		for (i = 0; i < XEON_NONLINK_DB_MSIX_BITS; i++) {
1222302493Smav			if ((ibits & ntb_db_vector_mask(dev, i)) != 0)
1223302493Smav				swi_sched(ntb->int_info[i].tag, 0);
1224302493Smav		}
1225302493Smav	} else {
1226302493Smav		db_iowrite(ntb, ntb->self_reg->db_mask, ntb->db_mask);
1227302493Smav	}
1228289546Scem	DB_MASK_UNLOCK(ntb);
1229289542Scem}
1230289542Scem
1231302484Smavstatic uint64_t
1232302484Smavntb_db_read(device_t dev)
1233289281Scem{
1234302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
1235289281Scem
1236302493Smav	if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP))
1237302493Smav		return (ntb->fake_db_bell);
1238295618Scem
1239289607Scem	return (db_ioread(ntb, ntb->self_reg->db_bell));
1240289281Scem}
1241289281Scem
1242302484Smavstatic void
1243302484Smavntb_db_clear(device_t dev, uint64_t bits)
1244289281Scem{
1245302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
1246289281Scem
1247289546Scem	KASSERT((bits & ~ntb->db_valid_mask) == 0,
1248289546Scem	    ("%s: Invalid bits 0x%jx (valid: 0x%jx)", __func__,
1249289546Scem	     (uintmax_t)(bits & ~ntb->db_valid_mask),
1250289546Scem	     (uintmax_t)ntb->db_valid_mask));
1251289546Scem
1252302484Smav	if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP)) {
1253302493Smav		DB_MASK_LOCK(ntb);
1254302493Smav		ntb->fake_db_bell &= ~bits;
1255302493Smav		DB_MASK_UNLOCK(ntb);
1256295618Scem		return;
1257295618Scem	}
1258295618Scem
1259289607Scem	db_iowrite(ntb, ntb->self_reg->db_bell, bits);
1260289281Scem}
1261289281Scem
1262289540Scemstatic inline uint64_t
1263289540Scemntb_vec_mask(struct ntb_softc *ntb, uint64_t db_vector)
1264250079Scarl{
1265289540Scem	uint64_t shift, mask;
1266250079Scarl
1267302493Smav	if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP)) {
1268302493Smav		/*
1269302493Smav		 * Remap vectors in custom way to make at least first
1270302493Smav		 * three doorbells to not generate stray events.
1271302493Smav		 * This breaks Linux compatibility (if one existed)
1272302493Smav		 * when more then one DB is used (not by if_ntb).
1273302493Smav		 */
1274302493Smav		if (db_vector < XEON_NONLINK_DB_MSIX_BITS - 1)
1275302493Smav			return (1 << db_vector);
1276302493Smav		if (db_vector == XEON_NONLINK_DB_MSIX_BITS - 1)
1277302493Smav			return (0x7ffc);
1278302493Smav	}
1279302493Smav
1280289540Scem	shift = ntb->db_vec_shift;
1281289540Scem	mask = (1ull << shift) - 1;
1282289540Scem	return (mask << (shift * db_vector));
1283250079Scarl}
1284250079Scarl
1285250079Scarlstatic void
1286289546Scemntb_interrupt(struct ntb_softc *ntb, uint32_t vec)
1287250079Scarl{
1288289540Scem	uint64_t vec_mask;
1289250079Scarl
1290289542Scem	ntb->last_ts = ticks;
1291289546Scem	vec_mask = ntb_vec_mask(ntb, vec);
1292250079Scarl
1293289542Scem	if ((vec_mask & ntb->db_link_mask) != 0) {
1294289546Scem		if (ntb_poll_link(ntb))
1295302484Smav			ntb_link_event(ntb->device);
1296289540Scem	}
1297289540Scem
1298302484Smav	if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP) &&
1299295618Scem	    (vec_mask & ntb->db_link_mask) == 0) {
1300295618Scem		DB_MASK_LOCK(ntb);
1301302493Smav
1302302493Smav		/* Do not report same DB events again if not cleared yet. */
1303302493Smav		vec_mask &= ~ntb->fake_db_bell;
1304302493Smav
1305302493Smav		/* Update our internal doorbell register. */
1306302493Smav		ntb->fake_db_bell |= vec_mask;
1307302493Smav
1308302493Smav		/* Do not report masked DB events. */
1309302493Smav		vec_mask &= ~ntb->db_mask;
1310302493Smav
1311295618Scem		DB_MASK_UNLOCK(ntb);
1312295618Scem	}
1313295618Scem
1314289546Scem	if ((vec_mask & ntb->db_valid_mask) != 0)
1315302484Smav		ntb_db_event(ntb->device, vec);
1316289546Scem}
1317250079Scarl
1318289546Scemstatic void
1319289546Scemndev_vec_isr(void *arg)
1320289546Scem{
1321289546Scem	struct ntb_vec *nvec = arg;
1322250079Scarl
1323289546Scem	ntb_interrupt(nvec->ntb, nvec->num);
1324250079Scarl}
1325250079Scarl
1326250079Scarlstatic void
1327289546Scemndev_irq_isr(void *arg)
1328250079Scarl{
1329289546Scem	/* If we couldn't set up MSI-X, we only have the one vector. */
1330289546Scem	ntb_interrupt(arg, 0);
1331250079Scarl}
1332250079Scarl
1333250079Scarlstatic int
1334289546Scemntb_create_msix_vec(struct ntb_softc *ntb, uint32_t num_vectors)
1335250079Scarl{
1336289342Scem	uint32_t i;
1337250079Scarl
1338289546Scem	ntb->msix_vec = malloc(num_vectors * sizeof(*ntb->msix_vec), M_NTB,
1339250079Scarl	    M_ZERO | M_WAITOK);
1340250079Scarl	for (i = 0; i < num_vectors; i++) {
1341289546Scem		ntb->msix_vec[i].num = i;
1342289546Scem		ntb->msix_vec[i].ntb = ntb;
1343250079Scarl	}
1344250079Scarl
1345250079Scarl	return (0);
1346250079Scarl}
1347250079Scarl
1348250079Scarlstatic void
1349289546Scemntb_free_msix_vec(struct ntb_softc *ntb)
1350250079Scarl{
1351250079Scarl
1352289546Scem	if (ntb->msix_vec == NULL)
1353289539Scem		return;
1354289539Scem
1355289546Scem	free(ntb->msix_vec, M_NTB);
1356289546Scem	ntb->msix_vec = NULL;
1357250079Scarl}
1358250079Scarl
1359295618Scemstatic void
1360300531Scemntb_get_msix_info(struct ntb_softc *ntb)
1361295618Scem{
1362295618Scem	struct pci_devinfo *dinfo;
1363295618Scem	struct pcicfg_msix *msix;
1364295618Scem	uint32_t laddr, data, i, offset;
1365295618Scem
1366295618Scem	dinfo = device_get_ivars(ntb->device);
1367295618Scem	msix = &dinfo->cfg.msix;
1368295618Scem
1369300531Scem	CTASSERT(XEON_NONLINK_DB_MSIX_BITS == nitems(ntb->msix_data));
1370300531Scem
1371300531Scem	for (i = 0; i < XEON_NONLINK_DB_MSIX_BITS; i++) {
1372295618Scem		offset = msix->msix_table_offset + i * PCI_MSIX_ENTRY_SIZE;
1373295618Scem
1374295618Scem		laddr = bus_read_4(msix->msix_table_res, offset +
1375295618Scem		    PCI_MSIX_ENTRY_LOWER_ADDR);
1376301293Smav		ntb_printf(2, "local MSIX addr(%u): 0x%x\n", i, laddr);
1377295618Scem
1378295618Scem		KASSERT((laddr & MSI_INTEL_ADDR_BASE) == MSI_INTEL_ADDR_BASE,
1379295618Scem		    ("local MSIX addr 0x%x not in MSI base 0x%x", laddr,
1380295618Scem		     MSI_INTEL_ADDR_BASE));
1381301293Smav		ntb->msix_data[i].nmd_ofs = laddr;
1382295618Scem
1383295618Scem		data = bus_read_4(msix->msix_table_res, offset +
1384295618Scem		    PCI_MSIX_ENTRY_DATA);
1385295618Scem		ntb_printf(2, "local MSIX data(%u): 0x%x\n", i, data);
1386295618Scem
1387295618Scem		ntb->msix_data[i].nmd_data = data;
1388295618Scem	}
1389295618Scem}
1390295618Scem
1391250079Scarlstatic struct ntb_hw_info *
1392250079Scarlntb_get_device_info(uint32_t device_id)
1393250079Scarl{
1394250079Scarl	struct ntb_hw_info *ep = pci_ids;
1395250079Scarl
1396250079Scarl	while (ep->device_id) {
1397250079Scarl		if (ep->device_id == device_id)
1398250079Scarl			return (ep);
1399250079Scarl		++ep;
1400250079Scarl	}
1401250079Scarl	return (NULL);
1402250079Scarl}
1403250079Scarl
1404289272Scemstatic void
1405289272Scemntb_teardown_xeon(struct ntb_softc *ntb)
1406250079Scarl{
1407250079Scarl
1408289617Scem	if (ntb->reg != NULL)
1409302484Smav		ntb_link_disable(ntb->device);
1410250079Scarl}
1411250079Scarl
1412289397Scemstatic void
1413289397Scemntb_detect_max_mw(struct ntb_softc *ntb)
1414289397Scem{
1415289397Scem
1416289648Scem	if (ntb->type == NTB_ATOM) {
1417289648Scem		ntb->mw_count = ATOM_MW_COUNT;
1418289397Scem		return;
1419289397Scem	}
1420289397Scem
1421302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR))
1422289539Scem		ntb->mw_count = XEON_HSX_SPLIT_MW_COUNT;
1423289397Scem	else
1424289539Scem		ntb->mw_count = XEON_SNB_MW_COUNT;
1425289397Scem}
1426289397Scem
1427250079Scarlstatic int
1428289348Scemntb_detect_xeon(struct ntb_softc *ntb)
1429250079Scarl{
1430289348Scem	uint8_t ppd, conn_type;
1431250079Scarl
1432289348Scem	ppd = pci_read_config(ntb->device, NTB_PPD_OFFSET, 1);
1433289348Scem	ntb->ppd = ppd;
1434250079Scarl
1435289348Scem	if ((ppd & XEON_PPD_DEV_TYPE) != 0)
1436290681Scem		ntb->dev_type = NTB_DEV_DSD;
1437290681Scem	else
1438289257Scem		ntb->dev_type = NTB_DEV_USD;
1439289257Scem
1440289397Scem	if ((ppd & XEON_PPD_SPLIT_BAR) != 0)
1441289397Scem		ntb->features |= NTB_SPLIT_BAR;
1442289397Scem
1443302508Smav	if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP) &&
1444302508Smav	    !HAS_FEATURE(ntb, NTB_SPLIT_BAR)) {
1445302508Smav		device_printf(ntb->device,
1446302508Smav		    "Can not apply SB01BASE_LOCKUP workaround "
1447302508Smav		    "with split BARs disabled!\n");
1448302508Smav		device_printf(ntb->device,
1449302508Smav		    "Expect system hangs under heavy NTB traffic!\n");
1450302508Smav		ntb->features &= ~NTB_SB01BASE_LOCKUP;
1451302508Smav	}
1452302508Smav
1453295618Scem	/*
1454295618Scem	 * SDOORBELL errata workaround gets in the way of SB01BASE_LOCKUP
1455295618Scem	 * errata workaround; only do one at a time.
1456295618Scem	 */
1457302484Smav	if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP))
1458295618Scem		ntb->features &= ~NTB_SDOORBELL_LOCKUP;
1459289542Scem
1460289348Scem	conn_type = ppd & XEON_PPD_CONN_TYPE;
1461289348Scem	switch (conn_type) {
1462289348Scem	case NTB_CONN_B2B:
1463289348Scem		ntb->conn_type = conn_type;
1464289348Scem		break;
1465289348Scem	case NTB_CONN_RP:
1466289348Scem	case NTB_CONN_TRANSPARENT:
1467289348Scem	default:
1468289348Scem		device_printf(ntb->device, "Unsupported connection type: %u\n",
1469289348Scem		    (unsigned)conn_type);
1470289348Scem		return (ENXIO);
1471289348Scem	}
1472289348Scem	return (0);
1473289348Scem}
1474289348Scem
1475289348Scemstatic int
1476289648Scemntb_detect_atom(struct ntb_softc *ntb)
1477289348Scem{
1478289348Scem	uint32_t ppd, conn_type;
1479289348Scem
1480289348Scem	ppd = pci_read_config(ntb->device, NTB_PPD_OFFSET, 4);
1481289348Scem	ntb->ppd = ppd;
1482289348Scem
1483289648Scem	if ((ppd & ATOM_PPD_DEV_TYPE) != 0)
1484289348Scem		ntb->dev_type = NTB_DEV_DSD;
1485289348Scem	else
1486289348Scem		ntb->dev_type = NTB_DEV_USD;
1487289348Scem
1488289648Scem	conn_type = (ppd & ATOM_PPD_CONN_TYPE) >> 8;
1489289348Scem	switch (conn_type) {
1490289348Scem	case NTB_CONN_B2B:
1491289348Scem		ntb->conn_type = conn_type;
1492289348Scem		break;
1493289348Scem	default:
1494289348Scem		device_printf(ntb->device, "Unsupported NTB configuration\n");
1495289348Scem		return (ENXIO);
1496289348Scem	}
1497289348Scem	return (0);
1498289348Scem}
1499289348Scem
1500289348Scemstatic int
1501289542Scemntb_xeon_init_dev(struct ntb_softc *ntb)
1502289348Scem{
1503289542Scem	int rc;
1504289348Scem
1505289542Scem	ntb->spad_count		= XEON_SPAD_COUNT;
1506289542Scem	ntb->db_count		= XEON_DB_COUNT;
1507289542Scem	ntb->db_link_mask	= XEON_DB_LINK_BIT;
1508289542Scem	ntb->db_vec_count	= XEON_DB_MSIX_VECTOR_COUNT;
1509289542Scem	ntb->db_vec_shift	= XEON_DB_MSIX_VECTOR_SHIFT;
1510289257Scem
1511289542Scem	if (ntb->conn_type != NTB_CONN_B2B) {
1512250079Scarl		device_printf(ntb->device, "Connection type %d not supported\n",
1513289348Scem		    ntb->conn_type);
1514250079Scarl		return (ENXIO);
1515250079Scarl	}
1516250079Scarl
1517289542Scem	ntb->reg = &xeon_reg;
1518289607Scem	ntb->self_reg = &xeon_pri_reg;
1519289542Scem	ntb->peer_reg = &xeon_b2b_reg;
1520289542Scem	ntb->xlat_reg = &xeon_sec_xlat;
1521289542Scem
1522302484Smav	if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP)) {
1523302493Smav		ntb->fake_db_bell = 0;
1524295618Scem		ntb->msix_mw_idx = (ntb->mw_count + g_ntb_msix_idx) %
1525295618Scem		    ntb->mw_count;
1526295618Scem		ntb_printf(2, "Setting up MSIX mw idx %d means %u\n",
1527295618Scem		    g_ntb_msix_idx, ntb->msix_mw_idx);
1528295618Scem		rc = ntb_mw_set_wc_internal(ntb, ntb->msix_mw_idx,
1529295618Scem		    VM_MEMATTR_UNCACHEABLE);
1530295618Scem		KASSERT(rc == 0, ("shouldn't fail"));
1531302484Smav	} else if (HAS_FEATURE(ntb, NTB_SDOORBELL_LOCKUP)) {
1532295618Scem		/*
1533295618Scem		 * There is a Xeon hardware errata related to writes to SDOORBELL or
1534295618Scem		 * B2BDOORBELL in conjunction with inbound access to NTB MMIO space,
1535295618Scem		 * which may hang the system.  To workaround this, use a memory
1536295618Scem		 * window to access the interrupt and scratch pad registers on the
1537295618Scem		 * remote system.
1538295618Scem		 */
1539291263Scem		ntb->b2b_mw_idx = (ntb->mw_count + g_ntb_mw_idx) %
1540291263Scem		    ntb->mw_count;
1541291263Scem		ntb_printf(2, "Setting up b2b mw idx %d means %u\n",
1542291263Scem		    g_ntb_mw_idx, ntb->b2b_mw_idx);
1543295618Scem		rc = ntb_mw_set_wc_internal(ntb, ntb->b2b_mw_idx,
1544295618Scem		    VM_MEMATTR_UNCACHEABLE);
1545291263Scem		KASSERT(rc == 0, ("shouldn't fail"));
1546302484Smav	} else if (HAS_FEATURE(ntb, NTB_B2BDOORBELL_BIT14))
1547289208Scem		/*
1548289542Scem		 * HW Errata on bit 14 of b2bdoorbell register.  Writes will not be
1549289542Scem		 * mirrored to the remote system.  Shrink the number of bits by one,
1550289542Scem		 * since bit 14 is the last bit.
1551289542Scem		 *
1552289542Scem		 * On REGS_THRU_MW errata mode, we don't use the b2bdoorbell register
1553289542Scem		 * anyway.  Nor for non-B2B connection types.
1554289542Scem		 */
1555289543Scem		ntb->db_count = XEON_DB_COUNT - 1;
1556250079Scarl
1557289542Scem	ntb->db_valid_mask = (1ull << ntb->db_count) - 1;
1558250079Scarl
1559289542Scem	if (ntb->dev_type == NTB_DEV_USD)
1560289542Scem		rc = xeon_setup_b2b_mw(ntb, &xeon_b2b_dsd_addr,
1561289542Scem		    &xeon_b2b_usd_addr);
1562289542Scem	else
1563289542Scem		rc = xeon_setup_b2b_mw(ntb, &xeon_b2b_usd_addr,
1564289542Scem		    &xeon_b2b_dsd_addr);
1565289542Scem	if (rc != 0)
1566289542Scem		return (rc);
1567289271Scem
1568250079Scarl	/* Enable Bus Master and Memory Space on the secondary side */
1569290682Scem	ntb_reg_write(2, XEON_SPCICMD_OFFSET,
1570289542Scem	    PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
1571255279Scarl
1572290682Scem	/*
1573290682Scem	 * Mask all doorbell interrupts.
1574290682Scem	 */
1575295618Scem	DB_MASK_LOCK(ntb);
1576295618Scem	ntb->db_mask = ntb->db_valid_mask;
1577295618Scem	db_iowrite(ntb, ntb->self_reg->db_mask, ntb->db_mask);
1578295618Scem	DB_MASK_UNLOCK(ntb);
1579250079Scarl
1580290682Scem	rc = ntb_init_isr(ntb);
1581290682Scem	return (rc);
1582250079Scarl}
1583250079Scarl
1584250079Scarlstatic int
1585289648Scemntb_atom_init_dev(struct ntb_softc *ntb)
1586250079Scarl{
1587290682Scem	int error;
1588250079Scarl
1589289348Scem	KASSERT(ntb->conn_type == NTB_CONN_B2B,
1590289348Scem	    ("Unsupported NTB configuration (%d)\n", ntb->conn_type));
1591250079Scarl
1592289648Scem	ntb->spad_count		 = ATOM_SPAD_COUNT;
1593289648Scem	ntb->db_count		 = ATOM_DB_COUNT;
1594289648Scem	ntb->db_vec_count	 = ATOM_DB_MSIX_VECTOR_COUNT;
1595289648Scem	ntb->db_vec_shift	 = ATOM_DB_MSIX_VECTOR_SHIFT;
1596289542Scem	ntb->db_valid_mask	 = (1ull << ntb->db_count) - 1;
1597250079Scarl
1598289648Scem	ntb->reg = &atom_reg;
1599289648Scem	ntb->self_reg = &atom_pri_reg;
1600289648Scem	ntb->peer_reg = &atom_b2b_reg;
1601289648Scem	ntb->xlat_reg = &atom_sec_xlat;
1602289542Scem
1603250079Scarl	/*
1604289648Scem	 * FIXME - MSI-X bug on early Atom HW, remove once internal issue is
1605250079Scarl	 * resolved.  Mask transaction layer internal parity errors.
1606250079Scarl	 */
1607250079Scarl	pci_write_config(ntb->device, 0xFC, 0x4, 4);
1608250079Scarl
1609289648Scem	configure_atom_secondary_side_bars(ntb);
1610250079Scarl
1611250079Scarl	/* Enable Bus Master and Memory Space on the secondary side */
1612290682Scem	ntb_reg_write(2, ATOM_SPCICMD_OFFSET,
1613250079Scarl	    PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
1614289209Scem
1615290682Scem	error = ntb_init_isr(ntb);
1616290682Scem	if (error != 0)
1617290682Scem		return (error);
1618290682Scem
1619289542Scem	/* Initiate PCI-E link training */
1620302484Smav	ntb_link_enable(ntb->device, NTB_SPEED_AUTO, NTB_WIDTH_AUTO);
1621250079Scarl
1622289648Scem	callout_reset(&ntb->heartbeat_timer, 0, atom_link_hb, ntb);
1623289542Scem
1624250079Scarl	return (0);
1625250079Scarl}
1626250079Scarl
1627289648Scem/* XXX: Linux driver doesn't seem to do any of this for Atom. */
1628255279Scarlstatic void
1629289648Scemconfigure_atom_secondary_side_bars(struct ntb_softc *ntb)
1630255279Scarl{
1631255279Scarl
1632255279Scarl	if (ntb->dev_type == NTB_DEV_USD) {
1633289648Scem		ntb_reg_write(8, ATOM_PBAR2XLAT_OFFSET,
1634290725Scem		    XEON_B2B_BAR2_ADDR64);
1635289648Scem		ntb_reg_write(8, ATOM_PBAR4XLAT_OFFSET,
1636290725Scem		    XEON_B2B_BAR4_ADDR64);
1637290725Scem		ntb_reg_write(8, ATOM_MBAR23_OFFSET, XEON_B2B_BAR2_ADDR64);
1638290725Scem		ntb_reg_write(8, ATOM_MBAR45_OFFSET, XEON_B2B_BAR4_ADDR64);
1639255279Scarl	} else {
1640289648Scem		ntb_reg_write(8, ATOM_PBAR2XLAT_OFFSET,
1641290725Scem		    XEON_B2B_BAR2_ADDR64);
1642289648Scem		ntb_reg_write(8, ATOM_PBAR4XLAT_OFFSET,
1643290725Scem		    XEON_B2B_BAR4_ADDR64);
1644290725Scem		ntb_reg_write(8, ATOM_MBAR23_OFFSET, XEON_B2B_BAR2_ADDR64);
1645290725Scem		ntb_reg_write(8, ATOM_MBAR45_OFFSET, XEON_B2B_BAR4_ADDR64);
1646255279Scarl	}
1647255279Scarl}
1648255279Scarl
1649289543Scem
1650289543Scem/*
1651289543Scem * When working around Xeon SDOORBELL errata by remapping remote registers in a
1652289543Scem * MW, limit the B2B MW to half a MW.  By sharing a MW, half the shared MW
1653289543Scem * remains for use by a higher layer.
1654289543Scem *
1655289543Scem * Will only be used if working around SDOORBELL errata and the BIOS-configured
1656289543Scem * MW size is sufficiently large.
1657289543Scem */
1658289543Scemstatic unsigned int ntb_b2b_mw_share;
1659289543ScemSYSCTL_UINT(_hw_ntb, OID_AUTO, b2b_mw_share, CTLFLAG_RDTUN, &ntb_b2b_mw_share,
1660289543Scem    0, "If enabled (non-zero), prefer to share half of the B2B peer register "
1661289543Scem    "MW with higher level consumers.  Both sides of the NTB MUST set the same "
1662289543Scem    "value here.");
1663289543Scem
1664289543Scemstatic void
1665289543Scemxeon_reset_sbar_size(struct ntb_softc *ntb, enum ntb_bar idx,
1666289543Scem    enum ntb_bar regbar)
1667289543Scem{
1668289543Scem	struct ntb_pci_bar_info *bar;
1669289543Scem	uint8_t bar_sz;
1670289543Scem
1671302484Smav	if (!HAS_FEATURE(ntb, NTB_SPLIT_BAR) && idx >= NTB_B2B_BAR_3)
1672289543Scem		return;
1673289543Scem
1674289543Scem	bar = &ntb->bar_info[idx];
1675289543Scem	bar_sz = pci_read_config(ntb->device, bar->psz_off, 1);
1676289543Scem	if (idx == regbar) {
1677289543Scem		if (ntb->b2b_off != 0)
1678289543Scem			bar_sz--;
1679289543Scem		else
1680289543Scem			bar_sz = 0;
1681289543Scem	}
1682289543Scem	pci_write_config(ntb->device, bar->ssz_off, bar_sz, 1);
1683289543Scem	bar_sz = pci_read_config(ntb->device, bar->ssz_off, 1);
1684289543Scem	(void)bar_sz;
1685289543Scem}
1686289543Scem
1687289543Scemstatic void
1688289546Scemxeon_set_sbar_base_and_limit(struct ntb_softc *ntb, uint64_t bar_addr,
1689289543Scem    enum ntb_bar idx, enum ntb_bar regbar)
1690289543Scem{
1691301293Smav	uint64_t reg_val;
1692289546Scem	uint32_t base_reg, lmt_reg;
1693289543Scem
1694289546Scem	bar_get_xlat_params(ntb, idx, &base_reg, NULL, &lmt_reg);
1695302482Smav	if (idx == regbar) {
1696302482Smav		if (ntb->b2b_off)
1697302482Smav			bar_addr += ntb->b2b_off;
1698302482Smav		else
1699302482Smav			bar_addr = 0;
1700302482Smav	}
1701289543Scem
1702289546Scem	if (!bar_is_64bit(ntb, idx)) {
1703302531Smav		ntb_reg_write(4, base_reg, bar_addr);
1704302531Smav		reg_val = ntb_reg_read(4, base_reg);
1705302531Smav		(void)reg_val;
1706302531Smav
1707301293Smav		ntb_reg_write(4, lmt_reg, bar_addr);
1708295618Scem		reg_val = ntb_reg_read(4, lmt_reg);
1709295618Scem		(void)reg_val;
1710302531Smav	} else {
1711302531Smav		ntb_reg_write(8, base_reg, bar_addr);
1712302531Smav		reg_val = ntb_reg_read(8, base_reg);
1713302531Smav		(void)reg_val;
1714295618Scem
1715301293Smav		ntb_reg_write(8, lmt_reg, bar_addr);
1716295618Scem		reg_val = ntb_reg_read(8, lmt_reg);
1717295618Scem		(void)reg_val;
1718289543Scem	}
1719289543Scem}
1720289543Scem
1721289543Scemstatic void
1722289543Scemxeon_set_pbar_xlat(struct ntb_softc *ntb, uint64_t base_addr, enum ntb_bar idx)
1723289543Scem{
1724289543Scem	struct ntb_pci_bar_info *bar;
1725289543Scem
1726289543Scem	bar = &ntb->bar_info[idx];
1727302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR) && idx >= NTB_B2B_BAR_2) {
1728289543Scem		ntb_reg_write(4, bar->pbarxlat_off, base_addr);
1729289543Scem		base_addr = ntb_reg_read(4, bar->pbarxlat_off);
1730289543Scem	} else {
1731289543Scem		ntb_reg_write(8, bar->pbarxlat_off, base_addr);
1732289543Scem		base_addr = ntb_reg_read(8, bar->pbarxlat_off);
1733289543Scem	}
1734289543Scem	(void)base_addr;
1735289543Scem}
1736289543Scem
1737289542Scemstatic int
1738289542Scemxeon_setup_b2b_mw(struct ntb_softc *ntb, const struct ntb_b2b_addr *addr,
1739289542Scem    const struct ntb_b2b_addr *peer_addr)
1740255279Scarl{
1741289543Scem	struct ntb_pci_bar_info *b2b_bar;
1742289543Scem	vm_size_t bar_size;
1743289543Scem	uint64_t bar_addr;
1744289543Scem	enum ntb_bar b2b_bar_num, i;
1745255279Scarl
1746289543Scem	if (ntb->b2b_mw_idx == B2B_MW_DISABLED) {
1747289543Scem		b2b_bar = NULL;
1748289543Scem		b2b_bar_num = NTB_CONFIG_BAR;
1749289543Scem		ntb->b2b_off = 0;
1750289543Scem	} else {
1751289543Scem		b2b_bar_num = ntb_mw_to_bar(ntb, ntb->b2b_mw_idx);
1752289543Scem		KASSERT(b2b_bar_num > 0 && b2b_bar_num < NTB_MAX_BARS,
1753289543Scem		    ("invalid b2b mw bar"));
1754289543Scem
1755289543Scem		b2b_bar = &ntb->bar_info[b2b_bar_num];
1756289543Scem		bar_size = b2b_bar->size;
1757289543Scem
1758289543Scem		if (ntb_b2b_mw_share != 0 &&
1759289543Scem		    (bar_size >> 1) >= XEON_B2B_MIN_SIZE)
1760289543Scem			ntb->b2b_off = bar_size >> 1;
1761289543Scem		else if (bar_size >= XEON_B2B_MIN_SIZE) {
1762289543Scem			ntb->b2b_off = 0;
1763289543Scem		} else {
1764289543Scem			device_printf(ntb->device,
1765289543Scem			    "B2B bar size is too small!\n");
1766289543Scem			return (EIO);
1767289543Scem		}
1768255279Scarl	}
1769289542Scem
1770289543Scem	/*
1771289543Scem	 * Reset the secondary bar sizes to match the primary bar sizes.
1772289543Scem	 * (Except, disable or halve the size of the B2B secondary bar.)
1773289543Scem	 */
1774289543Scem	for (i = NTB_B2B_BAR_1; i < NTB_MAX_BARS; i++)
1775289543Scem		xeon_reset_sbar_size(ntb, i, b2b_bar_num);
1776289543Scem
1777289543Scem	bar_addr = 0;
1778289543Scem	if (b2b_bar_num == NTB_CONFIG_BAR)
1779289543Scem		bar_addr = addr->bar0_addr;
1780289543Scem	else if (b2b_bar_num == NTB_B2B_BAR_1)
1781289543Scem		bar_addr = addr->bar2_addr64;
1782302484Smav	else if (b2b_bar_num == NTB_B2B_BAR_2 && !HAS_FEATURE(ntb, NTB_SPLIT_BAR))
1783289543Scem		bar_addr = addr->bar4_addr64;
1784289543Scem	else if (b2b_bar_num == NTB_B2B_BAR_2)
1785289543Scem		bar_addr = addr->bar4_addr32;
1786289543Scem	else if (b2b_bar_num == NTB_B2B_BAR_3)
1787289543Scem		bar_addr = addr->bar5_addr32;
1788289543Scem	else
1789289543Scem		KASSERT(false, ("invalid bar"));
1790289543Scem
1791289543Scem	ntb_reg_write(8, XEON_SBAR0BASE_OFFSET, bar_addr);
1792289543Scem
1793289543Scem	/*
1794289543Scem	 * Other SBARs are normally hit by the PBAR xlat, except for the b2b
1795289543Scem	 * register BAR.  The B2B BAR is either disabled above or configured
1796289543Scem	 * half-size.  It starts at PBAR xlat + offset.
1797289543Scem	 *
1798289543Scem	 * Also set up incoming BAR limits == base (zero length window).
1799289543Scem	 */
1800289543Scem	xeon_set_sbar_base_and_limit(ntb, addr->bar2_addr64, NTB_B2B_BAR_1,
1801289543Scem	    b2b_bar_num);
1802302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR)) {
1803289543Scem		xeon_set_sbar_base_and_limit(ntb, addr->bar4_addr32,
1804289543Scem		    NTB_B2B_BAR_2, b2b_bar_num);
1805289543Scem		xeon_set_sbar_base_and_limit(ntb, addr->bar5_addr32,
1806289543Scem		    NTB_B2B_BAR_3, b2b_bar_num);
1807289542Scem	} else
1808289543Scem		xeon_set_sbar_base_and_limit(ntb, addr->bar4_addr64,
1809289543Scem		    NTB_B2B_BAR_2, b2b_bar_num);
1810289543Scem
1811289543Scem	/* Zero incoming translation addrs */
1812289543Scem	ntb_reg_write(8, XEON_SBAR2XLAT_OFFSET, 0);
1813289543Scem	ntb_reg_write(8, XEON_SBAR4XLAT_OFFSET, 0);
1814289543Scem
1815302484Smav	if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP)) {
1816302510Smav		uint32_t xlat_reg, lmt_reg;
1817302493Smav		enum ntb_bar bar_num;
1818295618Scem
1819295618Scem		/*
1820295618Scem		 * We point the chosen MSIX MW BAR xlat to remote LAPIC for
1821295618Scem		 * workaround
1822295618Scem		 */
1823302510Smav		bar_num = ntb_mw_to_bar(ntb, ntb->msix_mw_idx);
1824302510Smav		bar_get_xlat_params(ntb, bar_num, NULL, &xlat_reg, &lmt_reg);
1825302510Smav		if (bar_is_64bit(ntb, bar_num)) {
1826302510Smav			ntb_reg_write(8, xlat_reg, MSI_INTEL_ADDR_BASE);
1827302510Smav			ntb->msix_xlat = ntb_reg_read(8, xlat_reg);
1828302510Smav			ntb_reg_write(8, lmt_reg, 0);
1829301293Smav		} else {
1830302510Smav			ntb_reg_write(4, xlat_reg, MSI_INTEL_ADDR_BASE);
1831302510Smav			ntb->msix_xlat = ntb_reg_read(4, xlat_reg);
1832302530Smav			ntb_reg_write(4, lmt_reg, 0);
1833301293Smav		}
1834302493Smav
1835302493Smav		ntb->peer_lapic_bar =  &ntb->bar_info[bar_num];
1836295618Scem	}
1837295618Scem	(void)ntb_reg_read(8, XEON_SBAR2XLAT_OFFSET);
1838295618Scem	(void)ntb_reg_read(8, XEON_SBAR4XLAT_OFFSET);
1839295618Scem
1840289543Scem	/* Zero outgoing translation limits (whole bar size windows) */
1841289543Scem	ntb_reg_write(8, XEON_PBAR2LMT_OFFSET, 0);
1842289543Scem	ntb_reg_write(8, XEON_PBAR4LMT_OFFSET, 0);
1843289543Scem
1844289543Scem	/* Set outgoing translation offsets */
1845289543Scem	xeon_set_pbar_xlat(ntb, peer_addr->bar2_addr64, NTB_B2B_BAR_1);
1846302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR)) {
1847289543Scem		xeon_set_pbar_xlat(ntb, peer_addr->bar4_addr32, NTB_B2B_BAR_2);
1848289543Scem		xeon_set_pbar_xlat(ntb, peer_addr->bar5_addr32, NTB_B2B_BAR_3);
1849289543Scem	} else
1850289543Scem		xeon_set_pbar_xlat(ntb, peer_addr->bar4_addr64, NTB_B2B_BAR_2);
1851289543Scem
1852289543Scem	/* Set the translation offset for B2B registers */
1853289543Scem	bar_addr = 0;
1854289543Scem	if (b2b_bar_num == NTB_CONFIG_BAR)
1855289543Scem		bar_addr = peer_addr->bar0_addr;
1856289543Scem	else if (b2b_bar_num == NTB_B2B_BAR_1)
1857289543Scem		bar_addr = peer_addr->bar2_addr64;
1858302484Smav	else if (b2b_bar_num == NTB_B2B_BAR_2 && !HAS_FEATURE(ntb, NTB_SPLIT_BAR))
1859289543Scem		bar_addr = peer_addr->bar4_addr64;
1860289543Scem	else if (b2b_bar_num == NTB_B2B_BAR_2)
1861289543Scem		bar_addr = peer_addr->bar4_addr32;
1862289543Scem	else if (b2b_bar_num == NTB_B2B_BAR_3)
1863289543Scem		bar_addr = peer_addr->bar5_addr32;
1864289543Scem	else
1865289543Scem		KASSERT(false, ("invalid bar"));
1866289543Scem
1867289543Scem	/*
1868289543Scem	 * B2B_XLAT_OFFSET is a 64-bit register but can only be written 32 bits
1869289543Scem	 * at a time.
1870289543Scem	 */
1871289543Scem	ntb_reg_write(4, XEON_B2B_XLAT_OFFSETL, bar_addr & 0xffffffff);
1872289543Scem	ntb_reg_write(4, XEON_B2B_XLAT_OFFSETU, bar_addr >> 32);
1873289542Scem	return (0);
1874255279Scarl}
1875255279Scarl
1876289546Scemstatic inline bool
1877295618Scem_xeon_link_is_up(struct ntb_softc *ntb)
1878295618Scem{
1879295618Scem
1880295618Scem	if (ntb->conn_type == NTB_CONN_TRANSPARENT)
1881295618Scem		return (true);
1882295618Scem	return ((ntb->lnk_sta & NTB_LINK_STATUS_ACTIVE) != 0);
1883295618Scem}
1884295618Scem
1885295618Scemstatic inline bool
1886289546Scemlink_is_up(struct ntb_softc *ntb)
1887289546Scem{
1888289546Scem
1889295618Scem	if (ntb->type == NTB_XEON)
1890295618Scem		return (_xeon_link_is_up(ntb) && (ntb->peer_msix_good ||
1891302484Smav		    !HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP)));
1892289546Scem
1893289648Scem	KASSERT(ntb->type == NTB_ATOM, ("ntb type"));
1894289648Scem	return ((ntb->ntb_ctl & ATOM_CNTL_LINK_DOWN) == 0);
1895289546Scem}
1896289546Scem
1897289546Scemstatic inline bool
1898289648Scematom_link_is_err(struct ntb_softc *ntb)
1899289546Scem{
1900289546Scem	uint32_t status;
1901289546Scem
1902289648Scem	KASSERT(ntb->type == NTB_ATOM, ("ntb type"));
1903289546Scem
1904289648Scem	status = ntb_reg_read(4, ATOM_LTSSMSTATEJMP_OFFSET);
1905289648Scem	if ((status & ATOM_LTSSMSTATEJMP_FORCEDETECT) != 0)
1906289546Scem		return (true);
1907289546Scem
1908289648Scem	status = ntb_reg_read(4, ATOM_IBSTERRRCRVSTS0_OFFSET);
1909289648Scem	return ((status & ATOM_IBIST_ERR_OFLOW) != 0);
1910289546Scem}
1911289546Scem
1912289648Scem/* Atom does not have link status interrupt, poll on that platform */
1913250079Scarlstatic void
1914289648Scematom_link_hb(void *arg)
1915250079Scarl{
1916250079Scarl	struct ntb_softc *ntb = arg;
1917289546Scem	sbintime_t timo, poll_ts;
1918250079Scarl
1919289546Scem	timo = NTB_HB_TIMEOUT * hz;
1920289546Scem	poll_ts = ntb->last_ts + timo;
1921289546Scem
1922289542Scem	/*
1923289542Scem	 * Delay polling the link status if an interrupt was received, unless
1924289542Scem	 * the cached link status says the link is down.
1925289542Scem	 */
1926289546Scem	if ((sbintime_t)ticks - poll_ts < 0 && link_is_up(ntb)) {
1927289546Scem		timo = poll_ts - ticks;
1928289542Scem		goto out;
1929289546Scem	}
1930289542Scem
1931289546Scem	if (ntb_poll_link(ntb))
1932302484Smav		ntb_link_event(ntb->device);
1933289542Scem
1934289648Scem	if (!link_is_up(ntb) && atom_link_is_err(ntb)) {
1935289546Scem		/* Link is down with error, proceed with recovery */
1936289648Scem		callout_reset(&ntb->lr_timer, 0, recover_atom_link, ntb);
1937289546Scem		return;
1938250079Scarl	}
1939250079Scarl
1940289542Scemout:
1941289648Scem	callout_reset(&ntb->heartbeat_timer, timo, atom_link_hb, ntb);
1942250079Scarl}
1943250079Scarl
1944250079Scarlstatic void
1945289648Scematom_perform_link_restart(struct ntb_softc *ntb)
1946250079Scarl{
1947250079Scarl	uint32_t status;
1948250079Scarl
1949250079Scarl	/* Driver resets the NTB ModPhy lanes - magic! */
1950289648Scem	ntb_reg_write(1, ATOM_MODPHY_PCSREG6, 0xe0);
1951289648Scem	ntb_reg_write(1, ATOM_MODPHY_PCSREG4, 0x40);
1952289648Scem	ntb_reg_write(1, ATOM_MODPHY_PCSREG4, 0x60);
1953289648Scem	ntb_reg_write(1, ATOM_MODPHY_PCSREG6, 0x60);
1954250079Scarl
1955250079Scarl	/* Driver waits 100ms to allow the NTB ModPhy to settle */
1956250079Scarl	pause("ModPhy", hz / 10);
1957250079Scarl
1958250079Scarl	/* Clear AER Errors, write to clear */
1959289648Scem	status = ntb_reg_read(4, ATOM_ERRCORSTS_OFFSET);
1960250079Scarl	status &= PCIM_AER_COR_REPLAY_ROLLOVER;
1961289648Scem	ntb_reg_write(4, ATOM_ERRCORSTS_OFFSET, status);
1962250079Scarl
1963250079Scarl	/* Clear unexpected electrical idle event in LTSSM, write to clear */
1964289648Scem	status = ntb_reg_read(4, ATOM_LTSSMERRSTS0_OFFSET);
1965289648Scem	status |= ATOM_LTSSMERRSTS0_UNEXPECTEDEI;
1966289648Scem	ntb_reg_write(4, ATOM_LTSSMERRSTS0_OFFSET, status);
1967250079Scarl
1968250079Scarl	/* Clear DeSkew Buffer error, write to clear */
1969289648Scem	status = ntb_reg_read(4, ATOM_DESKEWSTS_OFFSET);
1970289648Scem	status |= ATOM_DESKEWSTS_DBERR;
1971289648Scem	ntb_reg_write(4, ATOM_DESKEWSTS_OFFSET, status);
1972250079Scarl
1973289648Scem	status = ntb_reg_read(4, ATOM_IBSTERRRCRVSTS0_OFFSET);
1974289648Scem	status &= ATOM_IBIST_ERR_OFLOW;
1975289648Scem	ntb_reg_write(4, ATOM_IBSTERRRCRVSTS0_OFFSET, status);
1976250079Scarl
1977250079Scarl	/* Releases the NTB state machine to allow the link to retrain */
1978289648Scem	status = ntb_reg_read(4, ATOM_LTSSMSTATEJMP_OFFSET);
1979289648Scem	status &= ~ATOM_LTSSMSTATEJMP_FORCEDETECT;
1980289648Scem	ntb_reg_write(4, ATOM_LTSSMSTATEJMP_OFFSET, status);
1981250079Scarl}
1982250079Scarl
1983302484Smavstatic int
1984302484Smavntb_set_ctx(device_t dev, void *ctx, const struct ntb_ctx_ops *ops)
1985250079Scarl{
1986302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
1987250079Scarl
1988289546Scem	if (ctx == NULL || ops == NULL)
1989289546Scem		return (EINVAL);
1990250079Scarl
1991302491Smav	rm_wlock(&ntb->ctx_lock);
1992289546Scem	if (ntb->ctx_ops != NULL) {
1993302491Smav		rm_wunlock(&ntb->ctx_lock);
1994289546Scem		return (EINVAL);
1995250079Scarl	}
1996289546Scem	ntb->ntb_ctx = ctx;
1997289546Scem	ntb->ctx_ops = ops;
1998302491Smav	rm_wunlock(&ntb->ctx_lock);
1999250079Scarl
2000289546Scem	return (0);
2001250079Scarl}
2002250079Scarl
2003289546Scem/*
2004289546Scem * It is expected that this will only be used from contexts where the ctx_lock
2005289546Scem * is not needed to protect ntb_ctx lifetime.
2006289546Scem */
2007302484Smavstatic void *
2008302484Smavntb_get_ctx(device_t dev, const struct ntb_ctx_ops **ops)
2009289546Scem{
2010302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2011289546Scem
2012289546Scem	KASSERT(ntb->ntb_ctx != NULL && ntb->ctx_ops != NULL, ("bogus"));
2013289546Scem	if (ops != NULL)
2014289546Scem		*ops = ntb->ctx_ops;
2015289546Scem	return (ntb->ntb_ctx);
2016289546Scem}
2017289546Scem
2018302484Smavstatic void
2019302484Smavntb_clear_ctx(device_t dev)
2020289546Scem{
2021302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2022289546Scem
2023302491Smav	rm_wlock(&ntb->ctx_lock);
2024289546Scem	ntb->ntb_ctx = NULL;
2025289546Scem	ntb->ctx_ops = NULL;
2026302491Smav	rm_wunlock(&ntb->ctx_lock);
2027289546Scem}
2028289546Scem
2029289546Scem/*
2030289546Scem * ntb_link_event() - notify driver context of a change in link status
2031289546Scem * @ntb:        NTB device context
2032289546Scem *
2033289546Scem * Notify the driver context that the link status may have changed.  The driver
2034289546Scem * should call ntb_link_is_up() to get the current status.
2035289546Scem */
2036302484Smavstatic void
2037302484Smavntb_link_event(device_t dev)
2038289546Scem{
2039302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2040302491Smav	struct rm_priotracker ctx_tracker;
2041289546Scem
2042302491Smav	rm_rlock(&ntb->ctx_lock, &ctx_tracker);
2043289546Scem	if (ntb->ctx_ops != NULL && ntb->ctx_ops->link_event != NULL)
2044289546Scem		ntb->ctx_ops->link_event(ntb->ntb_ctx);
2045302491Smav	rm_runlock(&ntb->ctx_lock, &ctx_tracker);
2046289546Scem}
2047289546Scem
2048289546Scem/*
2049289546Scem * ntb_db_event() - notify driver context of a doorbell event
2050289546Scem * @ntb:        NTB device context
2051289546Scem * @vector:     Interrupt vector number
2052289546Scem *
2053289546Scem * Notify the driver context of a doorbell event.  If hardware supports
2054289546Scem * multiple interrupt vectors for doorbells, the vector number indicates which
2055289546Scem * vector received the interrupt.  The vector number is relative to the first
2056289546Scem * vector used for doorbells, starting at zero, and must be less than
2057289546Scem * ntb_db_vector_count().  The driver may call ntb_db_read() to check which
2058289546Scem * doorbell bits need service, and ntb_db_vector_mask() to determine which of
2059289546Scem * those bits are associated with the vector number.
2060289546Scem */
2061250079Scarlstatic void
2062302484Smavntb_db_event(device_t dev, uint32_t vec)
2063289272Scem{
2064302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2065302491Smav	struct rm_priotracker ctx_tracker;
2066289546Scem
2067302491Smav	rm_rlock(&ntb->ctx_lock, &ctx_tracker);
2068289546Scem	if (ntb->ctx_ops != NULL && ntb->ctx_ops->db_event != NULL)
2069289546Scem		ntb->ctx_ops->db_event(ntb->ntb_ctx, vec);
2070302491Smav	rm_runlock(&ntb->ctx_lock, &ctx_tracker);
2071289546Scem}
2072289546Scem
2073302484Smavstatic int
2074302484Smavntb_link_enable(device_t dev, enum ntb_speed speed __unused,
2075302484Smav    enum ntb_width width __unused)
2076289546Scem{
2077302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2078289280Scem	uint32_t cntl;
2079289272Scem
2080300100Scem	ntb_printf(2, "%s\n", __func__);
2081300100Scem
2082289648Scem	if (ntb->type == NTB_ATOM) {
2083289542Scem		pci_write_config(ntb->device, NTB_PPD_OFFSET,
2084289648Scem		    ntb->ppd | ATOM_PPD_INIT_LINK, 4);
2085289546Scem		return (0);
2086289542Scem	}
2087289542Scem
2088289280Scem	if (ntb->conn_type == NTB_CONN_TRANSPARENT) {
2089302484Smav		ntb_link_event(dev);
2090289546Scem		return (0);
2091289280Scem	}
2092289280Scem
2093289542Scem	cntl = ntb_reg_read(4, ntb->reg->ntb_ctl);
2094289280Scem	cntl &= ~(NTB_CNTL_LINK_DISABLE | NTB_CNTL_CFG_LOCK);
2095289280Scem	cntl |= NTB_CNTL_P2S_BAR23_SNOOP | NTB_CNTL_S2P_BAR23_SNOOP;
2096289397Scem	cntl |= NTB_CNTL_P2S_BAR4_SNOOP | NTB_CNTL_S2P_BAR4_SNOOP;
2097302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR))
2098289397Scem		cntl |= NTB_CNTL_P2S_BAR5_SNOOP | NTB_CNTL_S2P_BAR5_SNOOP;
2099289542Scem	ntb_reg_write(4, ntb->reg->ntb_ctl, cntl);
2100289546Scem	return (0);
2101289272Scem}
2102289272Scem
2103302484Smavstatic int
2104302484Smavntb_link_disable(device_t dev)
2105289272Scem{
2106302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2107289272Scem	uint32_t cntl;
2108289272Scem
2109300100Scem	ntb_printf(2, "%s\n", __func__);
2110300100Scem
2111289272Scem	if (ntb->conn_type == NTB_CONN_TRANSPARENT) {
2112302484Smav		ntb_link_event(dev);
2113289546Scem		return (0);
2114289272Scem	}
2115289272Scem
2116289542Scem	cntl = ntb_reg_read(4, ntb->reg->ntb_ctl);
2117289280Scem	cntl &= ~(NTB_CNTL_P2S_BAR23_SNOOP | NTB_CNTL_S2P_BAR23_SNOOP);
2118289397Scem	cntl &= ~(NTB_CNTL_P2S_BAR4_SNOOP | NTB_CNTL_S2P_BAR4_SNOOP);
2119302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR))
2120289397Scem		cntl &= ~(NTB_CNTL_P2S_BAR5_SNOOP | NTB_CNTL_S2P_BAR5_SNOOP);
2121289280Scem	cntl |= NTB_CNTL_LINK_DISABLE | NTB_CNTL_CFG_LOCK;
2122289542Scem	ntb_reg_write(4, ntb->reg->ntb_ctl, cntl);
2123289546Scem	return (0);
2124289272Scem}
2125289272Scem
2126302484Smavstatic bool
2127302484Smavntb_link_enabled(device_t dev)
2128300100Scem{
2129302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2130300100Scem	uint32_t cntl;
2131300100Scem
2132300100Scem	if (ntb->type == NTB_ATOM) {
2133300100Scem		cntl = pci_read_config(ntb->device, NTB_PPD_OFFSET, 4);
2134300100Scem		return ((cntl & ATOM_PPD_INIT_LINK) != 0);
2135300100Scem	}
2136300100Scem
2137300100Scem	if (ntb->conn_type == NTB_CONN_TRANSPARENT)
2138300100Scem		return (true);
2139300100Scem
2140300100Scem	cntl = ntb_reg_read(4, ntb->reg->ntb_ctl);
2141300100Scem	return ((cntl & NTB_CNTL_LINK_DISABLE) == 0);
2142300100Scem}
2143300100Scem
2144289272Scemstatic void
2145289648Scemrecover_atom_link(void *arg)
2146250079Scarl{
2147250079Scarl	struct ntb_softc *ntb = arg;
2148289608Scem	unsigned speed, width, oldspeed, oldwidth;
2149250079Scarl	uint32_t status32;
2150250079Scarl
2151289648Scem	atom_perform_link_restart(ntb);
2152250079Scarl
2153289232Scem	/*
2154289232Scem	 * There is a potential race between the 2 NTB devices recovering at
2155289232Scem	 * the same time.  If the times are the same, the link will not recover
2156289232Scem	 * and the driver will be stuck in this loop forever.  Add a random
2157289232Scem	 * interval to the recovery time to prevent this race.
2158289232Scem	 */
2159289648Scem	status32 = arc4random() % ATOM_LINK_RECOVERY_TIME;
2160289648Scem	pause("Link", (ATOM_LINK_RECOVERY_TIME + status32) * hz / 1000);
2161289232Scem
2162289648Scem	if (atom_link_is_err(ntb))
2163250079Scarl		goto retry;
2164250079Scarl
2165289542Scem	status32 = ntb_reg_read(4, ntb->reg->ntb_ctl);
2166289648Scem	if ((status32 & ATOM_CNTL_LINK_DOWN) != 0)
2167289232Scem		goto out;
2168289232Scem
2169289542Scem	status32 = ntb_reg_read(4, ntb->reg->lnk_sta);
2170289608Scem	width = NTB_LNK_STA_WIDTH(status32);
2171289608Scem	speed = status32 & NTB_LINK_SPEED_MASK;
2172289608Scem
2173289608Scem	oldwidth = NTB_LNK_STA_WIDTH(ntb->lnk_sta);
2174289608Scem	oldspeed = ntb->lnk_sta & NTB_LINK_SPEED_MASK;
2175289608Scem	if (oldwidth != width || oldspeed != speed)
2176250079Scarl		goto retry;
2177250079Scarl
2178289232Scemout:
2179289648Scem	callout_reset(&ntb->heartbeat_timer, NTB_HB_TIMEOUT * hz, atom_link_hb,
2180289542Scem	    ntb);
2181250079Scarl	return;
2182250079Scarl
2183250079Scarlretry:
2184289648Scem	callout_reset(&ntb->lr_timer, NTB_HB_TIMEOUT * hz, recover_atom_link,
2185250079Scarl	    ntb);
2186250079Scarl}
2187250079Scarl
2188289546Scem/*
2189289546Scem * Polls the HW link status register(s); returns true if something has changed.
2190289546Scem */
2191289546Scemstatic bool
2192289542Scemntb_poll_link(struct ntb_softc *ntb)
2193250079Scarl{
2194250079Scarl	uint32_t ntb_cntl;
2195289546Scem	uint16_t reg_val;
2196250079Scarl
2197289648Scem	if (ntb->type == NTB_ATOM) {
2198289542Scem		ntb_cntl = ntb_reg_read(4, ntb->reg->ntb_ctl);
2199289546Scem		if (ntb_cntl == ntb->ntb_ctl)
2200289546Scem			return (false);
2201289546Scem
2202289542Scem		ntb->ntb_ctl = ntb_cntl;
2203289542Scem		ntb->lnk_sta = ntb_reg_read(4, ntb->reg->lnk_sta);
2204250079Scarl	} else {
2205290678Scem		db_iowrite_raw(ntb, ntb->self_reg->db_bell, ntb->db_link_mask);
2206250079Scarl
2207289546Scem		reg_val = pci_read_config(ntb->device, ntb->reg->lnk_sta, 2);
2208289546Scem		if (reg_val == ntb->lnk_sta)
2209289546Scem			return (false);
2210250079Scarl
2211289546Scem		ntb->lnk_sta = reg_val;
2212295618Scem
2213302484Smav		if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP)) {
2214295618Scem			if (_xeon_link_is_up(ntb)) {
2215295618Scem				if (!ntb->peer_msix_good) {
2216295618Scem					callout_reset(&ntb->peer_msix_work, 0,
2217295618Scem					    ntb_exchange_msix, ntb);
2218295618Scem					return (false);
2219295618Scem				}
2220295618Scem			} else {
2221295618Scem				ntb->peer_msix_good = false;
2222295618Scem				ntb->peer_msix_done = false;
2223295618Scem			}
2224295618Scem		}
2225289542Scem	}
2226289546Scem	return (true);
2227289542Scem}
2228289542Scem
2229289546Scemstatic inline enum ntb_speed
2230289546Scemntb_link_sta_speed(struct ntb_softc *ntb)
2231250079Scarl{
2232250079Scarl
2233289546Scem	if (!link_is_up(ntb))
2234289546Scem		return (NTB_SPEED_NONE);
2235289546Scem	return (ntb->lnk_sta & NTB_LINK_SPEED_MASK);
2236250079Scarl}
2237250079Scarl
2238289546Scemstatic inline enum ntb_width
2239289546Scemntb_link_sta_width(struct ntb_softc *ntb)
2240250079Scarl{
2241250079Scarl
2242289546Scem	if (!link_is_up(ntb))
2243289546Scem		return (NTB_WIDTH_NONE);
2244289546Scem	return (NTB_LNK_STA_WIDTH(ntb->lnk_sta));
2245250079Scarl}
2246250079Scarl
2247289774ScemSYSCTL_NODE(_hw_ntb, OID_AUTO, debug_info, CTLFLAG_RW, 0,
2248289774Scem    "Driver state, statistics, and HW registers");
2249289774Scem
2250289774Scem#define NTB_REGSZ_MASK	(3ul << 30)
2251289774Scem#define NTB_REG_64	(1ul << 30)
2252289774Scem#define NTB_REG_32	(2ul << 30)
2253289774Scem#define NTB_REG_16	(3ul << 30)
2254289774Scem#define NTB_REG_8	(0ul << 30)
2255289774Scem
2256289774Scem#define NTB_DB_READ	(1ul << 29)
2257289774Scem#define NTB_PCI_REG	(1ul << 28)
2258289774Scem#define NTB_REGFLAGS_MASK	(NTB_REGSZ_MASK | NTB_DB_READ | NTB_PCI_REG)
2259289774Scem
2260289774Scemstatic void
2261289774Scemntb_sysctl_init(struct ntb_softc *ntb)
2262289774Scem{
2263300100Scem	struct sysctl_oid_list *globals, *tree_par, *regpar, *statpar, *errpar;
2264289774Scem	struct sysctl_ctx_list *ctx;
2265289774Scem	struct sysctl_oid *tree, *tmptree;
2266289774Scem
2267289774Scem	ctx = device_get_sysctl_ctx(ntb->device);
2268300100Scem	globals = SYSCTL_CHILDREN(device_get_sysctl_tree(ntb->device));
2269289774Scem
2270300100Scem	SYSCTL_ADD_PROC(ctx, globals, OID_AUTO, "link_status",
2271300100Scem	    CTLFLAG_RD | CTLTYPE_STRING, ntb, 0,
2272300100Scem	    sysctl_handle_link_status_human, "A",
2273300100Scem	    "Link status (human readable)");
2274300100Scem	SYSCTL_ADD_PROC(ctx, globals, OID_AUTO, "active",
2275300100Scem	    CTLFLAG_RD | CTLTYPE_UINT, ntb, 0, sysctl_handle_link_status,
2276300100Scem	    "IU", "Link status (1=active, 0=inactive)");
2277300100Scem	SYSCTL_ADD_PROC(ctx, globals, OID_AUTO, "admin_up",
2278300100Scem	    CTLFLAG_RW | CTLTYPE_UINT, ntb, 0, sysctl_handle_link_admin,
2279300100Scem	    "IU", "Set/get interface status (1=UP, 0=DOWN)");
2280300100Scem
2281300100Scem	tree = SYSCTL_ADD_NODE(ctx, globals, OID_AUTO, "debug_info",
2282300100Scem	    CTLFLAG_RD, NULL, "Driver state, statistics, and HW registers");
2283289774Scem	tree_par = SYSCTL_CHILDREN(tree);
2284289774Scem
2285289774Scem	SYSCTL_ADD_UINT(ctx, tree_par, OID_AUTO, "conn_type", CTLFLAG_RD,
2286289774Scem	    &ntb->conn_type, 0, "0 - Transparent; 1 - B2B; 2 - Root Port");
2287289774Scem	SYSCTL_ADD_UINT(ctx, tree_par, OID_AUTO, "dev_type", CTLFLAG_RD,
2288289774Scem	    &ntb->dev_type, 0, "0 - USD; 1 - DSD");
2289290687Scem	SYSCTL_ADD_UINT(ctx, tree_par, OID_AUTO, "ppd", CTLFLAG_RD,
2290290687Scem	    &ntb->ppd, 0, "Raw PPD register (cached)");
2291289774Scem
2292289774Scem	if (ntb->b2b_mw_idx != B2B_MW_DISABLED) {
2293289774Scem		SYSCTL_ADD_U8(ctx, tree_par, OID_AUTO, "b2b_idx", CTLFLAG_RD,
2294289774Scem		    &ntb->b2b_mw_idx, 0,
2295289774Scem		    "Index of the MW used for B2B remote register access");
2296289774Scem		SYSCTL_ADD_UQUAD(ctx, tree_par, OID_AUTO, "b2b_off",
2297289774Scem		    CTLFLAG_RD, &ntb->b2b_off,
2298289774Scem		    "If non-zero, offset of B2B register region in shared MW");
2299289774Scem	}
2300289774Scem
2301289774Scem	SYSCTL_ADD_PROC(ctx, tree_par, OID_AUTO, "features",
2302289774Scem	    CTLFLAG_RD | CTLTYPE_STRING, ntb, 0, sysctl_handle_features, "A",
2303289774Scem	    "Features/errata of this NTB device");
2304289774Scem
2305289774Scem	SYSCTL_ADD_UINT(ctx, tree_par, OID_AUTO, "ntb_ctl", CTLFLAG_RD,
2306290686Scem	    __DEVOLATILE(uint32_t *, &ntb->ntb_ctl), 0,
2307290686Scem	    "NTB CTL register (cached)");
2308289774Scem	SYSCTL_ADD_UINT(ctx, tree_par, OID_AUTO, "lnk_sta", CTLFLAG_RD,
2309290686Scem	    __DEVOLATILE(uint32_t *, &ntb->lnk_sta), 0,
2310290686Scem	    "LNK STA register (cached)");
2311289774Scem
2312289774Scem	SYSCTL_ADD_U8(ctx, tree_par, OID_AUTO, "mw_count", CTLFLAG_RD,
2313291263Scem	    &ntb->mw_count, 0, "MW count");
2314289774Scem	SYSCTL_ADD_U8(ctx, tree_par, OID_AUTO, "spad_count", CTLFLAG_RD,
2315289774Scem	    &ntb->spad_count, 0, "Scratchpad count");
2316289774Scem	SYSCTL_ADD_U8(ctx, tree_par, OID_AUTO, "db_count", CTLFLAG_RD,
2317289774Scem	    &ntb->db_count, 0, "Doorbell count");
2318289774Scem	SYSCTL_ADD_U8(ctx, tree_par, OID_AUTO, "db_vec_count", CTLFLAG_RD,
2319289774Scem	    &ntb->db_vec_count, 0, "Doorbell vector count");
2320289774Scem	SYSCTL_ADD_U8(ctx, tree_par, OID_AUTO, "db_vec_shift", CTLFLAG_RD,
2321289774Scem	    &ntb->db_vec_shift, 0, "Doorbell vector shift");
2322289774Scem
2323289774Scem	SYSCTL_ADD_UQUAD(ctx, tree_par, OID_AUTO, "db_valid_mask", CTLFLAG_RD,
2324289774Scem	    &ntb->db_valid_mask, "Doorbell valid mask");
2325289774Scem	SYSCTL_ADD_UQUAD(ctx, tree_par, OID_AUTO, "db_link_mask", CTLFLAG_RD,
2326289774Scem	    &ntb->db_link_mask, "Doorbell link mask");
2327289774Scem	SYSCTL_ADD_UQUAD(ctx, tree_par, OID_AUTO, "db_mask", CTLFLAG_RD,
2328289774Scem	    &ntb->db_mask, "Doorbell mask (cached)");
2329289774Scem
2330289774Scem	tmptree = SYSCTL_ADD_NODE(ctx, tree_par, OID_AUTO, "registers",
2331289774Scem	    CTLFLAG_RD, NULL, "Raw HW registers (big-endian)");
2332289774Scem	regpar = SYSCTL_CHILDREN(tmptree);
2333289774Scem
2334290682Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "ntbcntl",
2335290682Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb, NTB_REG_32 |
2336290682Scem	    ntb->reg->ntb_ctl, sysctl_handle_register, "IU",
2337290682Scem	    "NTB Control register");
2338290682Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "lnkcap",
2339290682Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb, NTB_REG_32 |
2340290682Scem	    0x19c, sysctl_handle_register, "IU",
2341290682Scem	    "NTB Link Capabilities");
2342290682Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "lnkcon",
2343290682Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb, NTB_REG_32 |
2344290682Scem	    0x1a0, sysctl_handle_register, "IU",
2345290682Scem	    "NTB Link Control register");
2346290682Scem
2347289774Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "db_mask",
2348289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2349289774Scem	    NTB_REG_64 | NTB_DB_READ | ntb->self_reg->db_mask,
2350289774Scem	    sysctl_handle_register, "QU", "Doorbell mask register");
2351289774Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "db_bell",
2352289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2353289774Scem	    NTB_REG_64 | NTB_DB_READ | ntb->self_reg->db_bell,
2354289774Scem	    sysctl_handle_register, "QU", "Doorbell register");
2355289774Scem
2356289774Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "incoming_xlat23",
2357289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2358289774Scem	    NTB_REG_64 | ntb->xlat_reg->bar2_xlat,
2359289774Scem	    sysctl_handle_register, "QU", "Incoming XLAT23 register");
2360302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR)) {
2361289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "incoming_xlat4",
2362289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2363289774Scem		    NTB_REG_32 | ntb->xlat_reg->bar4_xlat,
2364289774Scem		    sysctl_handle_register, "IU", "Incoming XLAT4 register");
2365289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "incoming_xlat5",
2366289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2367289774Scem		    NTB_REG_32 | ntb->xlat_reg->bar5_xlat,
2368289774Scem		    sysctl_handle_register, "IU", "Incoming XLAT5 register");
2369289774Scem	} else {
2370289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "incoming_xlat45",
2371289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2372289774Scem		    NTB_REG_64 | ntb->xlat_reg->bar4_xlat,
2373289774Scem		    sysctl_handle_register, "QU", "Incoming XLAT45 register");
2374289774Scem	}
2375289774Scem
2376289774Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "incoming_lmt23",
2377289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2378289774Scem	    NTB_REG_64 | ntb->xlat_reg->bar2_limit,
2379289774Scem	    sysctl_handle_register, "QU", "Incoming LMT23 register");
2380302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR)) {
2381289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "incoming_lmt4",
2382289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2383289774Scem		    NTB_REG_32 | ntb->xlat_reg->bar4_limit,
2384289774Scem		    sysctl_handle_register, "IU", "Incoming LMT4 register");
2385289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "incoming_lmt5",
2386289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2387289774Scem		    NTB_REG_32 | ntb->xlat_reg->bar5_limit,
2388289774Scem		    sysctl_handle_register, "IU", "Incoming LMT5 register");
2389289774Scem	} else {
2390289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "incoming_lmt45",
2391289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2392289774Scem		    NTB_REG_64 | ntb->xlat_reg->bar4_limit,
2393289774Scem		    sysctl_handle_register, "QU", "Incoming LMT45 register");
2394289774Scem	}
2395289774Scem
2396289774Scem	if (ntb->type == NTB_ATOM)
2397289774Scem		return;
2398289774Scem
2399289774Scem	tmptree = SYSCTL_ADD_NODE(ctx, regpar, OID_AUTO, "xeon_stats",
2400289774Scem	    CTLFLAG_RD, NULL, "Xeon HW statistics");
2401289774Scem	statpar = SYSCTL_CHILDREN(tmptree);
2402289774Scem	SYSCTL_ADD_PROC(ctx, statpar, OID_AUTO, "upstream_mem_miss",
2403289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2404289774Scem	    NTB_REG_16 | XEON_USMEMMISS_OFFSET,
2405289774Scem	    sysctl_handle_register, "SU", "Upstream Memory Miss");
2406289774Scem
2407289774Scem	tmptree = SYSCTL_ADD_NODE(ctx, regpar, OID_AUTO, "xeon_hw_err",
2408289774Scem	    CTLFLAG_RD, NULL, "Xeon HW errors");
2409289774Scem	errpar = SYSCTL_CHILDREN(tmptree);
2410289774Scem
2411290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "ppd",
2412289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2413290687Scem	    NTB_REG_8 | NTB_PCI_REG | NTB_PPD_OFFSET,
2414290687Scem	    sysctl_handle_register, "CU", "PPD");
2415290687Scem
2416290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "pbar23_sz",
2417290687Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2418290687Scem	    NTB_REG_8 | NTB_PCI_REG | XEON_PBAR23SZ_OFFSET,
2419290687Scem	    sysctl_handle_register, "CU", "PBAR23 SZ (log2)");
2420290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "pbar4_sz",
2421290687Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2422290687Scem	    NTB_REG_8 | NTB_PCI_REG | XEON_PBAR4SZ_OFFSET,
2423290687Scem	    sysctl_handle_register, "CU", "PBAR4 SZ (log2)");
2424290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "pbar5_sz",
2425290687Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2426290687Scem	    NTB_REG_8 | NTB_PCI_REG | XEON_PBAR5SZ_OFFSET,
2427290687Scem	    sysctl_handle_register, "CU", "PBAR5 SZ (log2)");
2428290687Scem
2429290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "sbar23_sz",
2430290687Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2431290687Scem	    NTB_REG_8 | NTB_PCI_REG | XEON_SBAR23SZ_OFFSET,
2432290687Scem	    sysctl_handle_register, "CU", "SBAR23 SZ (log2)");
2433290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "sbar4_sz",
2434290687Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2435290687Scem	    NTB_REG_8 | NTB_PCI_REG | XEON_SBAR4SZ_OFFSET,
2436290687Scem	    sysctl_handle_register, "CU", "SBAR4 SZ (log2)");
2437290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "sbar5_sz",
2438290687Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2439290687Scem	    NTB_REG_8 | NTB_PCI_REG | XEON_SBAR5SZ_OFFSET,
2440290687Scem	    sysctl_handle_register, "CU", "SBAR5 SZ (log2)");
2441290687Scem
2442290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "devsts",
2443290687Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2444289774Scem	    NTB_REG_16 | NTB_PCI_REG | XEON_DEVSTS_OFFSET,
2445289774Scem	    sysctl_handle_register, "SU", "DEVSTS");
2446290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "lnksts",
2447289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2448289774Scem	    NTB_REG_16 | NTB_PCI_REG | XEON_LINK_STATUS_OFFSET,
2449289774Scem	    sysctl_handle_register, "SU", "LNKSTS");
2450290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "slnksts",
2451290687Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2452290687Scem	    NTB_REG_16 | NTB_PCI_REG | XEON_SLINK_STATUS_OFFSET,
2453290687Scem	    sysctl_handle_register, "SU", "SLNKSTS");
2454290687Scem
2455289774Scem	SYSCTL_ADD_PROC(ctx, errpar, OID_AUTO, "uncerrsts",
2456289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2457289774Scem	    NTB_REG_32 | NTB_PCI_REG | XEON_UNCERRSTS_OFFSET,
2458289774Scem	    sysctl_handle_register, "IU", "UNCERRSTS");
2459289774Scem	SYSCTL_ADD_PROC(ctx, errpar, OID_AUTO, "corerrsts",
2460289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2461289774Scem	    NTB_REG_32 | NTB_PCI_REG | XEON_CORERRSTS_OFFSET,
2462289774Scem	    sysctl_handle_register, "IU", "CORERRSTS");
2463289774Scem
2464289774Scem	if (ntb->conn_type != NTB_CONN_B2B)
2465289774Scem		return;
2466289774Scem
2467289774Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "outgoing_xlat23",
2468289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2469289774Scem	    NTB_REG_64 | ntb->bar_info[NTB_B2B_BAR_1].pbarxlat_off,
2470289774Scem	    sysctl_handle_register, "QU", "Outgoing XLAT23 register");
2471302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR)) {
2472289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "outgoing_xlat4",
2473289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2474289774Scem		    NTB_REG_32 | ntb->bar_info[NTB_B2B_BAR_2].pbarxlat_off,
2475289774Scem		    sysctl_handle_register, "IU", "Outgoing XLAT4 register");
2476289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "outgoing_xlat5",
2477289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2478289774Scem		    NTB_REG_32 | ntb->bar_info[NTB_B2B_BAR_3].pbarxlat_off,
2479289774Scem		    sysctl_handle_register, "IU", "Outgoing XLAT5 register");
2480289774Scem	} else {
2481289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "outgoing_xlat45",
2482289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2483289774Scem		    NTB_REG_64 | ntb->bar_info[NTB_B2B_BAR_2].pbarxlat_off,
2484289774Scem		    sysctl_handle_register, "QU", "Outgoing XLAT45 register");
2485289774Scem	}
2486289774Scem
2487289774Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "outgoing_lmt23",
2488289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2489289774Scem	    NTB_REG_64 | XEON_PBAR2LMT_OFFSET,
2490289774Scem	    sysctl_handle_register, "QU", "Outgoing LMT23 register");
2491302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR)) {
2492289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "outgoing_lmt4",
2493289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2494289774Scem		    NTB_REG_32 | XEON_PBAR4LMT_OFFSET,
2495289774Scem		    sysctl_handle_register, "IU", "Outgoing LMT4 register");
2496289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "outgoing_lmt5",
2497289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2498289774Scem		    NTB_REG_32 | XEON_PBAR5LMT_OFFSET,
2499289774Scem		    sysctl_handle_register, "IU", "Outgoing LMT5 register");
2500289774Scem	} else {
2501289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "outgoing_lmt45",
2502289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2503289774Scem		    NTB_REG_64 | XEON_PBAR4LMT_OFFSET,
2504289774Scem		    sysctl_handle_register, "QU", "Outgoing LMT45 register");
2505289774Scem	}
2506289774Scem
2507289774Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "sbar01_base",
2508289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2509289774Scem	    NTB_REG_64 | ntb->xlat_reg->bar0_base,
2510289774Scem	    sysctl_handle_register, "QU", "Secondary BAR01 base register");
2511289774Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "sbar23_base",
2512289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2513289774Scem	    NTB_REG_64 | ntb->xlat_reg->bar2_base,
2514289774Scem	    sysctl_handle_register, "QU", "Secondary BAR23 base register");
2515302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR)) {
2516289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "sbar4_base",
2517289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2518289774Scem		    NTB_REG_32 | ntb->xlat_reg->bar4_base,
2519289774Scem		    sysctl_handle_register, "IU",
2520289774Scem		    "Secondary BAR4 base register");
2521289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "sbar5_base",
2522289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2523289774Scem		    NTB_REG_32 | ntb->xlat_reg->bar5_base,
2524289774Scem		    sysctl_handle_register, "IU",
2525289774Scem		    "Secondary BAR5 base register");
2526289774Scem	} else {
2527289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "sbar45_base",
2528289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2529289774Scem		    NTB_REG_64 | ntb->xlat_reg->bar4_base,
2530289774Scem		    sysctl_handle_register, "QU",
2531289774Scem		    "Secondary BAR45 base register");
2532289774Scem	}
2533289774Scem}
2534289774Scem
2535289774Scemstatic int
2536289774Scemsysctl_handle_features(SYSCTL_HANDLER_ARGS)
2537289774Scem{
2538302483Smav	struct ntb_softc *ntb = arg1;
2539289774Scem	struct sbuf sb;
2540289774Scem	int error;
2541289774Scem
2542289774Scem	sbuf_new_for_sysctl(&sb, NULL, 256, req);
2543289774Scem
2544289774Scem	sbuf_printf(&sb, "%b", ntb->features, NTB_FEATURES_STR);
2545289774Scem	error = sbuf_finish(&sb);
2546289774Scem	sbuf_delete(&sb);
2547289774Scem
2548289774Scem	if (error || !req->newptr)
2549289774Scem		return (error);
2550289774Scem	return (EINVAL);
2551289774Scem}
2552289774Scem
2553289774Scemstatic int
2554300100Scemsysctl_handle_link_admin(SYSCTL_HANDLER_ARGS)
2555289774Scem{
2556302483Smav	struct ntb_softc *ntb = arg1;
2557300100Scem	unsigned old, new;
2558300100Scem	int error;
2559300100Scem
2560302484Smav	old = ntb_link_enabled(ntb->device);
2561300100Scem
2562300100Scem	error = SYSCTL_OUT(req, &old, sizeof(old));
2563300100Scem	if (error != 0 || req->newptr == NULL)
2564300100Scem		return (error);
2565300100Scem
2566300100Scem	error = SYSCTL_IN(req, &new, sizeof(new));
2567300100Scem	if (error != 0)
2568300100Scem		return (error);
2569300100Scem
2570300100Scem	ntb_printf(0, "Admin set interface state to '%sabled'\n",
2571300100Scem	    (new != 0)? "en" : "dis");
2572300100Scem
2573300100Scem	if (new != 0)
2574302484Smav		error = ntb_link_enable(ntb->device, NTB_SPEED_AUTO, NTB_WIDTH_AUTO);
2575300100Scem	else
2576302484Smav		error = ntb_link_disable(ntb->device);
2577300100Scem	return (error);
2578300100Scem}
2579300100Scem
2580300100Scemstatic int
2581300100Scemsysctl_handle_link_status_human(SYSCTL_HANDLER_ARGS)
2582300100Scem{
2583302483Smav	struct ntb_softc *ntb = arg1;
2584289774Scem	struct sbuf sb;
2585289774Scem	enum ntb_speed speed;
2586289774Scem	enum ntb_width width;
2587289774Scem	int error;
2588289774Scem
2589289774Scem	sbuf_new_for_sysctl(&sb, NULL, 32, req);
2590289774Scem
2591302484Smav	if (ntb_link_is_up(ntb->device, &speed, &width))
2592289774Scem		sbuf_printf(&sb, "up / PCIe Gen %u / Width x%u",
2593289774Scem		    (unsigned)speed, (unsigned)width);
2594289774Scem	else
2595289774Scem		sbuf_printf(&sb, "down");
2596289774Scem
2597289774Scem	error = sbuf_finish(&sb);
2598289774Scem	sbuf_delete(&sb);
2599289774Scem
2600289774Scem	if (error || !req->newptr)
2601289774Scem		return (error);
2602289774Scem	return (EINVAL);
2603289774Scem}
2604289774Scem
2605289774Scemstatic int
2606300100Scemsysctl_handle_link_status(SYSCTL_HANDLER_ARGS)
2607300100Scem{
2608302483Smav	struct ntb_softc *ntb = arg1;
2609300100Scem	unsigned res;
2610300100Scem	int error;
2611300100Scem
2612302484Smav	res = ntb_link_is_up(ntb->device, NULL, NULL);
2613300100Scem
2614300100Scem	error = SYSCTL_OUT(req, &res, sizeof(res));
2615300100Scem	if (error || !req->newptr)
2616300100Scem		return (error);
2617300100Scem	return (EINVAL);
2618300100Scem}
2619300100Scem
2620300100Scemstatic int
2621289774Scemsysctl_handle_register(SYSCTL_HANDLER_ARGS)
2622289774Scem{
2623289774Scem	struct ntb_softc *ntb;
2624289774Scem	const void *outp;
2625289774Scem	uintptr_t sz;
2626289774Scem	uint64_t umv;
2627289774Scem	char be[sizeof(umv)];
2628289774Scem	size_t outsz;
2629289774Scem	uint32_t reg;
2630289774Scem	bool db, pci;
2631289774Scem	int error;
2632289774Scem
2633289774Scem	ntb = arg1;
2634289774Scem	reg = arg2 & ~NTB_REGFLAGS_MASK;
2635289774Scem	sz = arg2 & NTB_REGSZ_MASK;
2636289774Scem	db = (arg2 & NTB_DB_READ) != 0;
2637289774Scem	pci = (arg2 & NTB_PCI_REG) != 0;
2638289774Scem
2639289774Scem	KASSERT(!(db && pci), ("bogus"));
2640289774Scem
2641289774Scem	if (db) {
2642289774Scem		KASSERT(sz == NTB_REG_64, ("bogus"));
2643289774Scem		umv = db_ioread(ntb, reg);
2644289774Scem		outsz = sizeof(uint64_t);
2645289774Scem	} else {
2646289774Scem		switch (sz) {
2647289774Scem		case NTB_REG_64:
2648289774Scem			if (pci)
2649289774Scem				umv = pci_read_config(ntb->device, reg, 8);
2650289774Scem			else
2651289774Scem				umv = ntb_reg_read(8, reg);
2652289774Scem			outsz = sizeof(uint64_t);
2653289774Scem			break;
2654289774Scem		case NTB_REG_32:
2655289774Scem			if (pci)
2656289774Scem				umv = pci_read_config(ntb->device, reg, 4);
2657289774Scem			else
2658289774Scem				umv = ntb_reg_read(4, reg);
2659289774Scem			outsz = sizeof(uint32_t);
2660289774Scem			break;
2661289774Scem		case NTB_REG_16:
2662289774Scem			if (pci)
2663289774Scem				umv = pci_read_config(ntb->device, reg, 2);
2664289774Scem			else
2665289774Scem				umv = ntb_reg_read(2, reg);
2666289774Scem			outsz = sizeof(uint16_t);
2667289774Scem			break;
2668289774Scem		case NTB_REG_8:
2669289774Scem			if (pci)
2670289774Scem				umv = pci_read_config(ntb->device, reg, 1);
2671289774Scem			else
2672289774Scem				umv = ntb_reg_read(1, reg);
2673289774Scem			outsz = sizeof(uint8_t);
2674289774Scem			break;
2675289774Scem		default:
2676289774Scem			panic("bogus");
2677289774Scem			break;
2678289774Scem		}
2679289774Scem	}
2680289774Scem
2681289774Scem	/* Encode bigendian so that sysctl -x is legible. */
2682289774Scem	be64enc(be, umv);
2683289774Scem	outp = ((char *)be) + sizeof(umv) - outsz;
2684289774Scem
2685289774Scem	error = SYSCTL_OUT(req, outp, outsz);
2686289774Scem	if (error || !req->newptr)
2687289774Scem		return (error);
2688289774Scem	return (EINVAL);
2689289774Scem}
2690289774Scem
2691291263Scemstatic unsigned
2692291263Scemntb_user_mw_to_idx(struct ntb_softc *ntb, unsigned uidx)
2693291263Scem{
2694291263Scem
2695295618Scem	if ((ntb->b2b_mw_idx != B2B_MW_DISABLED && ntb->b2b_off == 0 &&
2696295618Scem	    uidx >= ntb->b2b_mw_idx) ||
2697295618Scem	    (ntb->msix_mw_idx != B2B_MW_DISABLED && uidx >= ntb->msix_mw_idx))
2698295618Scem		uidx++;
2699295618Scem	if ((ntb->b2b_mw_idx != B2B_MW_DISABLED && ntb->b2b_off == 0 &&
2700295618Scem	    uidx >= ntb->b2b_mw_idx) &&
2701295618Scem	    (ntb->msix_mw_idx != B2B_MW_DISABLED && uidx >= ntb->msix_mw_idx))
2702295618Scem		uidx++;
2703291263Scem	return (uidx);
2704291263Scem}
2705291263Scem
2706295618Scemstatic void
2707295618Scemntb_exchange_msix(void *ctx)
2708295618Scem{
2709295618Scem	struct ntb_softc *ntb;
2710295618Scem	uint32_t val;
2711295618Scem	unsigned i;
2712295618Scem
2713295618Scem	ntb = ctx;
2714295618Scem
2715301292Smav	if (ntb->peer_msix_good)
2716301292Smav		goto msix_good;
2717295618Scem	if (ntb->peer_msix_done)
2718295618Scem		goto msix_done;
2719295618Scem
2720295618Scem	for (i = 0; i < XEON_NONLINK_DB_MSIX_BITS; i++) {
2721302484Smav		ntb_peer_spad_write(ntb->device, NTB_MSIX_DATA0 + i,
2722295618Scem		    ntb->msix_data[i].nmd_data);
2723302484Smav		ntb_peer_spad_write(ntb->device, NTB_MSIX_OFS0 + i,
2724301293Smav		    ntb->msix_data[i].nmd_ofs - ntb->msix_xlat);
2725295618Scem	}
2726302484Smav	ntb_peer_spad_write(ntb->device, NTB_MSIX_GUARD, NTB_MSIX_VER_GUARD);
2727295618Scem
2728302484Smav	ntb_spad_read(ntb->device, NTB_MSIX_GUARD, &val);
2729295618Scem	if (val != NTB_MSIX_VER_GUARD)
2730295618Scem		goto reschedule;
2731295618Scem
2732295618Scem	for (i = 0; i < XEON_NONLINK_DB_MSIX_BITS; i++) {
2733302484Smav		ntb_spad_read(ntb->device, NTB_MSIX_DATA0 + i, &val);
2734301293Smav		ntb_printf(2, "remote MSIX data(%u): 0x%x\n", i, val);
2735295618Scem		ntb->peer_msix_data[i].nmd_data = val;
2736302484Smav		ntb_spad_read(ntb->device, NTB_MSIX_OFS0 + i, &val);
2737301293Smav		ntb_printf(2, "remote MSIX addr(%u): 0x%x\n", i, val);
2738295618Scem		ntb->peer_msix_data[i].nmd_ofs = val;
2739295618Scem	}
2740295618Scem
2741295618Scem	ntb->peer_msix_done = true;
2742295618Scem
2743295618Scemmsix_done:
2744302484Smav	ntb_peer_spad_write(ntb->device, NTB_MSIX_DONE, NTB_MSIX_RECEIVED);
2745302484Smav	ntb_spad_read(ntb->device, NTB_MSIX_DONE, &val);
2746295618Scem	if (val != NTB_MSIX_RECEIVED)
2747295618Scem		goto reschedule;
2748295618Scem
2749295618Scem	ntb->peer_msix_good = true;
2750301292Smav	/* Give peer time to see our NTB_MSIX_RECEIVED. */
2751301292Smav	goto reschedule;
2752295618Scem
2753301292Smavmsix_good:
2754295618Scem	ntb_poll_link(ntb);
2755302484Smav	ntb_link_event(ntb->device);
2756295618Scem	return;
2757295618Scem
2758295618Scemreschedule:
2759295618Scem	ntb->lnk_sta = pci_read_config(ntb->device, ntb->reg->lnk_sta, 2);
2760301292Smav	if (_xeon_link_is_up(ntb)) {
2761301292Smav		callout_reset(&ntb->peer_msix_work,
2762301292Smav		    hz * (ntb->peer_msix_good ? 2 : 1) / 100,
2763301292Smav		    ntb_exchange_msix, ntb);
2764301292Smav	} else
2765302484Smav		ntb_spad_clear(ntb->device);
2766295618Scem}
2767295618Scem
2768289546Scem/*
2769289546Scem * Public API to the rest of the OS
2770250079Scarl */
2771250079Scarl
2772302484Smavstatic uint8_t
2773302484Smavntb_spad_count(device_t dev)
2774250079Scarl{
2775302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2776250079Scarl
2777289539Scem	return (ntb->spad_count);
2778250079Scarl}
2779250079Scarl
2780302484Smavstatic uint8_t
2781302484Smavntb_mw_count(device_t dev)
2782289396Scem{
2783302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2784295618Scem	uint8_t res;
2785289396Scem
2786295618Scem	res = ntb->mw_count;
2787291263Scem	if (ntb->b2b_mw_idx != B2B_MW_DISABLED && ntb->b2b_off == 0)
2788295618Scem		res--;
2789295618Scem	if (ntb->msix_mw_idx != B2B_MW_DISABLED)
2790295618Scem		res--;
2791295618Scem	return (res);
2792289396Scem}
2793289396Scem
2794302484Smavstatic int
2795302484Smavntb_spad_write(device_t dev, unsigned int idx, uint32_t val)
2796250079Scarl{
2797302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2798250079Scarl
2799289539Scem	if (idx >= ntb->spad_count)
2800250079Scarl		return (EINVAL);
2801250079Scarl
2802289607Scem	ntb_reg_write(4, ntb->self_reg->spad + idx * 4, val);
2803250079Scarl
2804250079Scarl	return (0);
2805250079Scarl}
2806250079Scarl
2807295618Scem/*
2808295618Scem * Zeros the local scratchpad.
2809295618Scem */
2810302484Smavstatic void
2811302484Smavntb_spad_clear(device_t dev)
2812295618Scem{
2813302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2814295618Scem	unsigned i;
2815295618Scem
2816295618Scem	for (i = 0; i < ntb->spad_count; i++)
2817302484Smav		ntb_spad_write(dev, i, 0);
2818295618Scem}
2819295618Scem
2820302484Smavstatic int
2821302484Smavntb_spad_read(device_t dev, unsigned int idx, uint32_t *val)
2822250079Scarl{
2823302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2824250079Scarl
2825289539Scem	if (idx >= ntb->spad_count)
2826250079Scarl		return (EINVAL);
2827250079Scarl
2828289607Scem	*val = ntb_reg_read(4, ntb->self_reg->spad + idx * 4);
2829250079Scarl
2830250079Scarl	return (0);
2831250079Scarl}
2832250079Scarl
2833302484Smavstatic int
2834302484Smavntb_peer_spad_write(device_t dev, unsigned int idx, uint32_t val)
2835250079Scarl{
2836302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2837250079Scarl
2838289539Scem	if (idx >= ntb->spad_count)
2839250079Scarl		return (EINVAL);
2840250079Scarl
2841302484Smav	if (HAS_FEATURE(ntb, NTB_SDOORBELL_LOCKUP))
2842290682Scem		ntb_mw_write(4, XEON_SPAD_OFFSET + idx * 4, val);
2843255279Scarl	else
2844289542Scem		ntb_reg_write(4, ntb->peer_reg->spad + idx * 4, val);
2845250079Scarl
2846250079Scarl	return (0);
2847250079Scarl}
2848250079Scarl
2849302484Smavstatic int
2850302484Smavntb_peer_spad_read(device_t dev, unsigned int idx, uint32_t *val)
2851250079Scarl{
2852302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2853250079Scarl
2854289539Scem	if (idx >= ntb->spad_count)
2855250079Scarl		return (EINVAL);
2856250079Scarl
2857302484Smav	if (HAS_FEATURE(ntb, NTB_SDOORBELL_LOCKUP))
2858290682Scem		*val = ntb_mw_read(4, XEON_SPAD_OFFSET + idx * 4);
2859255279Scarl	else
2860289542Scem		*val = ntb_reg_read(4, ntb->peer_reg->spad + idx * 4);
2861250079Scarl
2862250079Scarl	return (0);
2863250079Scarl}
2864250079Scarl
2865302484Smavstatic int
2866302484Smavntb_mw_get_range(device_t dev, unsigned mw_idx, vm_paddr_t *base,
2867291033Scem    caddr_t *vbase, size_t *size, size_t *align, size_t *align_size,
2868291033Scem    bus_addr_t *plimit)
2869250079Scarl{
2870302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2871289546Scem	struct ntb_pci_bar_info *bar;
2872291033Scem	bus_addr_t limit;
2873289546Scem	size_t bar_b2b_off;
2874291033Scem	enum ntb_bar bar_num;
2875250079Scarl
2876302484Smav	if (mw_idx >= ntb_mw_count(dev))
2877289546Scem		return (EINVAL);
2878291263Scem	mw_idx = ntb_user_mw_to_idx(ntb, mw_idx);
2879250079Scarl
2880291033Scem	bar_num = ntb_mw_to_bar(ntb, mw_idx);
2881291033Scem	bar = &ntb->bar_info[bar_num];
2882289546Scem	bar_b2b_off = 0;
2883289546Scem	if (mw_idx == ntb->b2b_mw_idx) {
2884289546Scem		KASSERT(ntb->b2b_off != 0,
2885289546Scem		    ("user shouldn't get non-shared b2b mw"));
2886289546Scem		bar_b2b_off = ntb->b2b_off;
2887289546Scem	}
2888250079Scarl
2889291033Scem	if (bar_is_64bit(ntb, bar_num))
2890291033Scem		limit = BUS_SPACE_MAXADDR;
2891291033Scem	else
2892291033Scem		limit = BUS_SPACE_MAXADDR_32BIT;
2893291033Scem
2894289546Scem	if (base != NULL)
2895289546Scem		*base = bar->pbase + bar_b2b_off;
2896289546Scem	if (vbase != NULL)
2897290679Scem		*vbase = bar->vbase + bar_b2b_off;
2898289546Scem	if (size != NULL)
2899289546Scem		*size = bar->size - bar_b2b_off;
2900289546Scem	if (align != NULL)
2901289546Scem		*align = bar->size;
2902289546Scem	if (align_size != NULL)
2903289546Scem		*align_size = 1;
2904291033Scem	if (plimit != NULL)
2905291033Scem		*plimit = limit;
2906289546Scem	return (0);
2907250079Scarl}
2908250079Scarl
2909302484Smavstatic int
2910302484Smavntb_mw_set_trans(device_t dev, unsigned idx, bus_addr_t addr, size_t size)
2911250079Scarl{
2912302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2913289546Scem	struct ntb_pci_bar_info *bar;
2914289546Scem	uint64_t base, limit, reg_val;
2915289546Scem	size_t bar_size, mw_size;
2916289546Scem	uint32_t base_reg, xlat_reg, limit_reg;
2917289546Scem	enum ntb_bar bar_num;
2918250079Scarl
2919302484Smav	if (idx >= ntb_mw_count(dev))
2920289546Scem		return (EINVAL);
2921291263Scem	idx = ntb_user_mw_to_idx(ntb, idx);
2922250079Scarl
2923289546Scem	bar_num = ntb_mw_to_bar(ntb, idx);
2924289546Scem	bar = &ntb->bar_info[bar_num];
2925250079Scarl
2926289546Scem	bar_size = bar->size;
2927289546Scem	if (idx == ntb->b2b_mw_idx)
2928289546Scem		mw_size = bar_size - ntb->b2b_off;
2929289546Scem	else
2930289546Scem		mw_size = bar_size;
2931250079Scarl
2932289546Scem	/* Hardware requires that addr is aligned to bar size */
2933289546Scem	if ((addr & (bar_size - 1)) != 0)
2934289546Scem		return (EINVAL);
2935250079Scarl
2936289546Scem	if (size > mw_size)
2937289546Scem		return (EINVAL);
2938289546Scem
2939289546Scem	bar_get_xlat_params(ntb, bar_num, &base_reg, &xlat_reg, &limit_reg);
2940289546Scem
2941289546Scem	limit = 0;
2942289546Scem	if (bar_is_64bit(ntb, bar_num)) {
2943291032Scem		base = ntb_reg_read(8, base_reg) & BAR_HIGH_MASK;
2944289546Scem
2945289546Scem		if (limit_reg != 0 && size != mw_size)
2946289546Scem			limit = base + size;
2947289546Scem
2948289546Scem		/* Set and verify translation address */
2949289546Scem		ntb_reg_write(8, xlat_reg, addr);
2950291032Scem		reg_val = ntb_reg_read(8, xlat_reg) & BAR_HIGH_MASK;
2951289546Scem		if (reg_val != addr) {
2952289546Scem			ntb_reg_write(8, xlat_reg, 0);
2953289546Scem			return (EIO);
2954289546Scem		}
2955289546Scem
2956289546Scem		/* Set and verify the limit */
2957289546Scem		ntb_reg_write(8, limit_reg, limit);
2958291032Scem		reg_val = ntb_reg_read(8, limit_reg) & BAR_HIGH_MASK;
2959289546Scem		if (reg_val != limit) {
2960289546Scem			ntb_reg_write(8, limit_reg, base);
2961289546Scem			ntb_reg_write(8, xlat_reg, 0);
2962289546Scem			return (EIO);
2963289546Scem		}
2964289546Scem	} else {
2965289546Scem		/* Configure 32-bit (split) BAR MW */
2966289546Scem
2967291029Scem		if ((addr & UINT32_MAX) != addr)
2968291033Scem			return (ERANGE);
2969291029Scem		if (((addr + size) & UINT32_MAX) != (addr + size))
2970291033Scem			return (ERANGE);
2971289546Scem
2972291032Scem		base = ntb_reg_read(4, base_reg) & BAR_HIGH_MASK;
2973289546Scem
2974289546Scem		if (limit_reg != 0 && size != mw_size)
2975289546Scem			limit = base + size;
2976289546Scem
2977289546Scem		/* Set and verify translation address */
2978289546Scem		ntb_reg_write(4, xlat_reg, addr);
2979291032Scem		reg_val = ntb_reg_read(4, xlat_reg) & BAR_HIGH_MASK;
2980289546Scem		if (reg_val != addr) {
2981289546Scem			ntb_reg_write(4, xlat_reg, 0);
2982289546Scem			return (EIO);
2983289546Scem		}
2984289546Scem
2985289546Scem		/* Set and verify the limit */
2986289546Scem		ntb_reg_write(4, limit_reg, limit);
2987291032Scem		reg_val = ntb_reg_read(4, limit_reg) & BAR_HIGH_MASK;
2988289546Scem		if (reg_val != limit) {
2989289546Scem			ntb_reg_write(4, limit_reg, base);
2990289546Scem			ntb_reg_write(4, xlat_reg, 0);
2991289546Scem			return (EIO);
2992289546Scem		}
2993250079Scarl	}
2994289546Scem	return (0);
2995250079Scarl}
2996250079Scarl
2997302484Smavstatic int
2998302484Smavntb_mw_clear_trans(device_t dev, unsigned mw_idx)
2999289596Scem{
3000289596Scem
3001302484Smav	return (ntb_mw_set_trans(dev, mw_idx, 0, 0));
3002289596Scem}
3003289596Scem
3004302484Smavstatic int
3005302484Smavntb_mw_get_wc(device_t dev, unsigned idx, vm_memattr_t *mode)
3006291031Scem{
3007302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
3008291031Scem	struct ntb_pci_bar_info *bar;
3009291031Scem
3010302484Smav	if (idx >= ntb_mw_count(dev))
3011291031Scem		return (EINVAL);
3012291263Scem	idx = ntb_user_mw_to_idx(ntb, idx);
3013291031Scem
3014291031Scem	bar = &ntb->bar_info[ntb_mw_to_bar(ntb, idx)];
3015291280Scem	*mode = bar->map_mode;
3016291031Scem	return (0);
3017291031Scem}
3018291031Scem
3019302484Smavstatic int
3020302484Smavntb_mw_set_wc(device_t dev, unsigned idx, vm_memattr_t mode)
3021291031Scem{
3022302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
3023291263Scem
3024302484Smav	if (idx >= ntb_mw_count(dev))
3025291263Scem		return (EINVAL);
3026291263Scem
3027291263Scem	idx = ntb_user_mw_to_idx(ntb, idx);
3028291280Scem	return (ntb_mw_set_wc_internal(ntb, idx, mode));
3029291263Scem}
3030291263Scem
3031291263Scemstatic int
3032291280Scemntb_mw_set_wc_internal(struct ntb_softc *ntb, unsigned idx, vm_memattr_t mode)
3033291263Scem{
3034291031Scem	struct ntb_pci_bar_info *bar;
3035291031Scem	int rc;
3036291031Scem
3037291031Scem	bar = &ntb->bar_info[ntb_mw_to_bar(ntb, idx)];
3038291280Scem	if (bar->map_mode == mode)
3039291031Scem		return (0);
3040291031Scem
3041291280Scem	rc = pmap_change_attr((vm_offset_t)bar->vbase, bar->size, mode);
3042291031Scem	if (rc == 0)
3043291280Scem		bar->map_mode = mode;
3044291031Scem
3045291031Scem	return (rc);
3046291031Scem}
3047291031Scem
3048302484Smavstatic void
3049302484Smavntb_peer_db_set(device_t dev, uint64_t bit)
3050250079Scarl{
3051302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
3052250079Scarl
3053302484Smav	if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP)) {
3054295618Scem		struct ntb_pci_bar_info *lapic;
3055295618Scem		unsigned i;
3056295618Scem
3057295618Scem		lapic = ntb->peer_lapic_bar;
3058295618Scem
3059295618Scem		for (i = 0; i < XEON_NONLINK_DB_MSIX_BITS; i++) {
3060302484Smav			if ((bit & ntb_db_vector_mask(dev, i)) != 0)
3061295618Scem				bus_space_write_4(lapic->pci_bus_tag,
3062295618Scem				    lapic->pci_bus_handle,
3063295618Scem				    ntb->peer_msix_data[i].nmd_ofs,
3064295618Scem				    ntb->peer_msix_data[i].nmd_data);
3065295618Scem		}
3066295618Scem		return;
3067295618Scem	}
3068295618Scem
3069302484Smav	if (HAS_FEATURE(ntb, NTB_SDOORBELL_LOCKUP)) {
3070290682Scem		ntb_mw_write(2, XEON_PDOORBELL_OFFSET, bit);
3071289347Scem		return;
3072289209Scem	}
3073289347Scem
3074289546Scem	db_iowrite(ntb, ntb->peer_reg->db_bell, bit);
3075250079Scarl}
3076250079Scarl
3077302484Smavstatic int
3078302484Smavntb_peer_db_addr(device_t dev, bus_addr_t *db_addr, vm_size_t *db_size)
3079289542Scem{
3080302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
3081289542Scem	struct ntb_pci_bar_info *bar;
3082289542Scem	uint64_t regoff;
3083289542Scem
3084302484Smav	KASSERT((db_addr != NULL && db_size != NULL), ("must be non-NULL"));
3085289542Scem
3086302484Smav	if (!HAS_FEATURE(ntb, NTB_SDOORBELL_LOCKUP)) {
3087289542Scem		bar = &ntb->bar_info[NTB_CONFIG_BAR];
3088289542Scem		regoff = ntb->peer_reg->db_bell;
3089289542Scem	} else {
3090289543Scem		KASSERT(ntb->b2b_mw_idx != B2B_MW_DISABLED,
3091289543Scem		    ("invalid b2b idx"));
3092289542Scem
3093289542Scem		bar = &ntb->bar_info[ntb_mw_to_bar(ntb, ntb->b2b_mw_idx)];
3094290682Scem		regoff = XEON_PDOORBELL_OFFSET;
3095289542Scem	}
3096289542Scem	KASSERT(bar->pci_bus_tag != X86_BUS_SPACE_IO, ("uh oh"));
3097289542Scem
3098289542Scem	/* HACK: Specific to current x86 bus implementation. */
3099302484Smav	*db_addr = ((uint64_t)bar->pci_bus_handle + regoff);
3100302484Smav	*db_size = ntb->reg->db_size;
3101302484Smav	return (0);
3102289542Scem}
3103289542Scem
3104302484Smavstatic uint64_t
3105302484Smavntb_db_valid_mask(device_t dev)
3106289597Scem{
3107302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
3108289597Scem
3109289597Scem	return (ntb->db_valid_mask);
3110289597Scem}
3111289597Scem
3112302484Smavstatic int
3113302484Smavntb_db_vector_count(device_t dev)
3114289598Scem{
3115302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
3116289598Scem
3117302484Smav	return (ntb->db_vec_count);
3118302484Smav}
3119302484Smav
3120302484Smavstatic uint64_t
3121302484Smavntb_db_vector_mask(device_t dev, uint32_t vector)
3122302484Smav{
3123302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
3124302484Smav
3125289598Scem	if (vector > ntb->db_vec_count)
3126289598Scem		return (0);
3127289598Scem	return (ntb->db_valid_mask & ntb_vec_mask(ntb, vector));
3128289598Scem}
3129289598Scem
3130302484Smavstatic bool
3131302484Smavntb_link_is_up(device_t dev, enum ntb_speed *speed, enum ntb_width *width)
3132250079Scarl{
3133302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
3134250079Scarl
3135289546Scem	if (speed != NULL)
3136289546Scem		*speed = ntb_link_sta_speed(ntb);
3137289546Scem	if (width != NULL)
3138289546Scem		*width = ntb_link_sta_width(ntb);
3139289546Scem	return (link_is_up(ntb));
3140250079Scarl}
3141250079Scarl
3142255272Scarlstatic void
3143255272Scarlsave_bar_parameters(struct ntb_pci_bar_info *bar)
3144250079Scarl{
3145255272Scarl
3146289209Scem	bar->pci_bus_tag = rman_get_bustag(bar->pci_resource);
3147289209Scem	bar->pci_bus_handle = rman_get_bushandle(bar->pci_resource);
3148289209Scem	bar->pbase = rman_get_start(bar->pci_resource);
3149289209Scem	bar->size = rman_get_size(bar->pci_resource);
3150289209Scem	bar->vbase = rman_get_virtual(bar->pci_resource);
3151250079Scarl}
3152255268Scarl
3153302484Smavstatic device_method_t ntb_intel_methods[] = {
3154302484Smav	/* Device interface */
3155302484Smav	DEVMETHOD(device_probe,     ntb_probe),
3156302484Smav	DEVMETHOD(device_attach,    ntb_attach),
3157302484Smav	DEVMETHOD(device_detach,    ntb_detach),
3158302484Smav	/* NTB interface */
3159302484Smav	DEVMETHOD(ntb_link_is_up,	ntb_link_is_up),
3160302484Smav	DEVMETHOD(ntb_link_enable,	ntb_link_enable),
3161302484Smav	DEVMETHOD(ntb_link_disable,	ntb_link_disable),
3162302484Smav	DEVMETHOD(ntb_link_enabled,	ntb_link_enabled),
3163302484Smav	DEVMETHOD(ntb_set_ctx,		ntb_set_ctx),
3164302484Smav	DEVMETHOD(ntb_get_ctx,		ntb_get_ctx),
3165302484Smav	DEVMETHOD(ntb_clear_ctx,	ntb_clear_ctx),
3166302484Smav	DEVMETHOD(ntb_mw_count,		ntb_mw_count),
3167302484Smav	DEVMETHOD(ntb_mw_get_range,	ntb_mw_get_range),
3168302484Smav	DEVMETHOD(ntb_mw_set_trans,	ntb_mw_set_trans),
3169302484Smav	DEVMETHOD(ntb_mw_clear_trans,	ntb_mw_clear_trans),
3170302484Smav	DEVMETHOD(ntb_mw_get_wc,	ntb_mw_get_wc),
3171302484Smav	DEVMETHOD(ntb_mw_set_wc,	ntb_mw_set_wc),
3172302484Smav	DEVMETHOD(ntb_spad_count,	ntb_spad_count),
3173302484Smav	DEVMETHOD(ntb_spad_clear,	ntb_spad_clear),
3174302484Smav	DEVMETHOD(ntb_spad_write,	ntb_spad_write),
3175302484Smav	DEVMETHOD(ntb_spad_read,	ntb_spad_read),
3176302484Smav	DEVMETHOD(ntb_peer_spad_write,	ntb_peer_spad_write),
3177302484Smav	DEVMETHOD(ntb_peer_spad_read,	ntb_peer_spad_read),
3178302484Smav	DEVMETHOD(ntb_db_valid_mask,	ntb_db_valid_mask),
3179302484Smav	DEVMETHOD(ntb_db_vector_count,	ntb_db_vector_count),
3180302484Smav	DEVMETHOD(ntb_db_vector_mask,	ntb_db_vector_mask),
3181302484Smav	DEVMETHOD(ntb_db_clear,		ntb_db_clear),
3182302484Smav	DEVMETHOD(ntb_db_clear_mask,	ntb_db_clear_mask),
3183302484Smav	DEVMETHOD(ntb_db_read,		ntb_db_read),
3184302484Smav	DEVMETHOD(ntb_db_set_mask,	ntb_db_set_mask),
3185302484Smav	DEVMETHOD(ntb_peer_db_addr,	ntb_peer_db_addr),
3186302484Smav	DEVMETHOD(ntb_peer_db_set,	ntb_peer_db_set),
3187302484Smav	DEVMETHOD_END
3188302484Smav};
3189255268Scarl
3190302484Smavstatic DEFINE_CLASS_0(ntb_hw, ntb_intel_driver, ntb_intel_methods,
3191302484Smav    sizeof(struct ntb_softc));
3192302484SmavDRIVER_MODULE(ntb_intel, pci, ntb_intel_driver, ntb_hw_devclass, NULL, NULL);
3193302484SmavMODULE_DEPEND(ntb_intel, ntb, 1, 1, 1);
3194302484SmavMODULE_VERSION(ntb_intel, 1);
3195