ntb_hw_intel.c revision 302493
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 302493 2016-07-09 11:57:21Z 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
453295618Scemstatic int g_ntb_msix_idx = 0;
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
1443295618Scem	/*
1444295618Scem	 * SDOORBELL errata workaround gets in the way of SB01BASE_LOCKUP
1445295618Scem	 * errata workaround; only do one at a time.
1446295618Scem	 */
1447302484Smav	if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP))
1448295618Scem		ntb->features &= ~NTB_SDOORBELL_LOCKUP;
1449289542Scem
1450289348Scem	conn_type = ppd & XEON_PPD_CONN_TYPE;
1451289348Scem	switch (conn_type) {
1452289348Scem	case NTB_CONN_B2B:
1453289348Scem		ntb->conn_type = conn_type;
1454289348Scem		break;
1455289348Scem	case NTB_CONN_RP:
1456289348Scem	case NTB_CONN_TRANSPARENT:
1457289348Scem	default:
1458289348Scem		device_printf(ntb->device, "Unsupported connection type: %u\n",
1459289348Scem		    (unsigned)conn_type);
1460289348Scem		return (ENXIO);
1461289348Scem	}
1462289348Scem	return (0);
1463289348Scem}
1464289348Scem
1465289348Scemstatic int
1466289648Scemntb_detect_atom(struct ntb_softc *ntb)
1467289348Scem{
1468289348Scem	uint32_t ppd, conn_type;
1469289348Scem
1470289348Scem	ppd = pci_read_config(ntb->device, NTB_PPD_OFFSET, 4);
1471289348Scem	ntb->ppd = ppd;
1472289348Scem
1473289648Scem	if ((ppd & ATOM_PPD_DEV_TYPE) != 0)
1474289348Scem		ntb->dev_type = NTB_DEV_DSD;
1475289348Scem	else
1476289348Scem		ntb->dev_type = NTB_DEV_USD;
1477289348Scem
1478289648Scem	conn_type = (ppd & ATOM_PPD_CONN_TYPE) >> 8;
1479289348Scem	switch (conn_type) {
1480289348Scem	case NTB_CONN_B2B:
1481289348Scem		ntb->conn_type = conn_type;
1482289348Scem		break;
1483289348Scem	default:
1484289348Scem		device_printf(ntb->device, "Unsupported NTB configuration\n");
1485289348Scem		return (ENXIO);
1486289348Scem	}
1487289348Scem	return (0);
1488289348Scem}
1489289348Scem
1490289348Scemstatic int
1491289542Scemntb_xeon_init_dev(struct ntb_softc *ntb)
1492289348Scem{
1493289542Scem	int rc;
1494289348Scem
1495289542Scem	ntb->spad_count		= XEON_SPAD_COUNT;
1496289542Scem	ntb->db_count		= XEON_DB_COUNT;
1497289542Scem	ntb->db_link_mask	= XEON_DB_LINK_BIT;
1498289542Scem	ntb->db_vec_count	= XEON_DB_MSIX_VECTOR_COUNT;
1499289542Scem	ntb->db_vec_shift	= XEON_DB_MSIX_VECTOR_SHIFT;
1500289257Scem
1501289542Scem	if (ntb->conn_type != NTB_CONN_B2B) {
1502250079Scarl		device_printf(ntb->device, "Connection type %d not supported\n",
1503289348Scem		    ntb->conn_type);
1504250079Scarl		return (ENXIO);
1505250079Scarl	}
1506250079Scarl
1507289542Scem	ntb->reg = &xeon_reg;
1508289607Scem	ntb->self_reg = &xeon_pri_reg;
1509289542Scem	ntb->peer_reg = &xeon_b2b_reg;
1510289542Scem	ntb->xlat_reg = &xeon_sec_xlat;
1511289542Scem
1512302484Smav	if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP)) {
1513302493Smav		ntb->fake_db_bell = 0;
1514295618Scem		ntb->msix_mw_idx = (ntb->mw_count + g_ntb_msix_idx) %
1515295618Scem		    ntb->mw_count;
1516295618Scem		ntb_printf(2, "Setting up MSIX mw idx %d means %u\n",
1517295618Scem		    g_ntb_msix_idx, ntb->msix_mw_idx);
1518295618Scem		rc = ntb_mw_set_wc_internal(ntb, ntb->msix_mw_idx,
1519295618Scem		    VM_MEMATTR_UNCACHEABLE);
1520295618Scem		KASSERT(rc == 0, ("shouldn't fail"));
1521302484Smav	} else if (HAS_FEATURE(ntb, NTB_SDOORBELL_LOCKUP)) {
1522295618Scem		/*
1523295618Scem		 * There is a Xeon hardware errata related to writes to SDOORBELL or
1524295618Scem		 * B2BDOORBELL in conjunction with inbound access to NTB MMIO space,
1525295618Scem		 * which may hang the system.  To workaround this, use a memory
1526295618Scem		 * window to access the interrupt and scratch pad registers on the
1527295618Scem		 * remote system.
1528295618Scem		 */
1529291263Scem		ntb->b2b_mw_idx = (ntb->mw_count + g_ntb_mw_idx) %
1530291263Scem		    ntb->mw_count;
1531291263Scem		ntb_printf(2, "Setting up b2b mw idx %d means %u\n",
1532291263Scem		    g_ntb_mw_idx, ntb->b2b_mw_idx);
1533295618Scem		rc = ntb_mw_set_wc_internal(ntb, ntb->b2b_mw_idx,
1534295618Scem		    VM_MEMATTR_UNCACHEABLE);
1535291263Scem		KASSERT(rc == 0, ("shouldn't fail"));
1536302484Smav	} else if (HAS_FEATURE(ntb, NTB_B2BDOORBELL_BIT14))
1537289208Scem		/*
1538289542Scem		 * HW Errata on bit 14 of b2bdoorbell register.  Writes will not be
1539289542Scem		 * mirrored to the remote system.  Shrink the number of bits by one,
1540289542Scem		 * since bit 14 is the last bit.
1541289542Scem		 *
1542289542Scem		 * On REGS_THRU_MW errata mode, we don't use the b2bdoorbell register
1543289542Scem		 * anyway.  Nor for non-B2B connection types.
1544289542Scem		 */
1545289543Scem		ntb->db_count = XEON_DB_COUNT - 1;
1546250079Scarl
1547289542Scem	ntb->db_valid_mask = (1ull << ntb->db_count) - 1;
1548250079Scarl
1549289542Scem	if (ntb->dev_type == NTB_DEV_USD)
1550289542Scem		rc = xeon_setup_b2b_mw(ntb, &xeon_b2b_dsd_addr,
1551289542Scem		    &xeon_b2b_usd_addr);
1552289542Scem	else
1553289542Scem		rc = xeon_setup_b2b_mw(ntb, &xeon_b2b_usd_addr,
1554289542Scem		    &xeon_b2b_dsd_addr);
1555289542Scem	if (rc != 0)
1556289542Scem		return (rc);
1557289271Scem
1558250079Scarl	/* Enable Bus Master and Memory Space on the secondary side */
1559290682Scem	ntb_reg_write(2, XEON_SPCICMD_OFFSET,
1560289542Scem	    PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
1561255279Scarl
1562290682Scem	/*
1563290682Scem	 * Mask all doorbell interrupts.
1564290682Scem	 */
1565295618Scem	DB_MASK_LOCK(ntb);
1566295618Scem	ntb->db_mask = ntb->db_valid_mask;
1567295618Scem	db_iowrite(ntb, ntb->self_reg->db_mask, ntb->db_mask);
1568295618Scem	DB_MASK_UNLOCK(ntb);
1569250079Scarl
1570290682Scem	rc = ntb_init_isr(ntb);
1571290682Scem	return (rc);
1572250079Scarl}
1573250079Scarl
1574250079Scarlstatic int
1575289648Scemntb_atom_init_dev(struct ntb_softc *ntb)
1576250079Scarl{
1577290682Scem	int error;
1578250079Scarl
1579289348Scem	KASSERT(ntb->conn_type == NTB_CONN_B2B,
1580289348Scem	    ("Unsupported NTB configuration (%d)\n", ntb->conn_type));
1581250079Scarl
1582289648Scem	ntb->spad_count		 = ATOM_SPAD_COUNT;
1583289648Scem	ntb->db_count		 = ATOM_DB_COUNT;
1584289648Scem	ntb->db_vec_count	 = ATOM_DB_MSIX_VECTOR_COUNT;
1585289648Scem	ntb->db_vec_shift	 = ATOM_DB_MSIX_VECTOR_SHIFT;
1586289542Scem	ntb->db_valid_mask	 = (1ull << ntb->db_count) - 1;
1587250079Scarl
1588289648Scem	ntb->reg = &atom_reg;
1589289648Scem	ntb->self_reg = &atom_pri_reg;
1590289648Scem	ntb->peer_reg = &atom_b2b_reg;
1591289648Scem	ntb->xlat_reg = &atom_sec_xlat;
1592289542Scem
1593250079Scarl	/*
1594289648Scem	 * FIXME - MSI-X bug on early Atom HW, remove once internal issue is
1595250079Scarl	 * resolved.  Mask transaction layer internal parity errors.
1596250079Scarl	 */
1597250079Scarl	pci_write_config(ntb->device, 0xFC, 0x4, 4);
1598250079Scarl
1599289648Scem	configure_atom_secondary_side_bars(ntb);
1600250079Scarl
1601250079Scarl	/* Enable Bus Master and Memory Space on the secondary side */
1602290682Scem	ntb_reg_write(2, ATOM_SPCICMD_OFFSET,
1603250079Scarl	    PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
1604289209Scem
1605290682Scem	error = ntb_init_isr(ntb);
1606290682Scem	if (error != 0)
1607290682Scem		return (error);
1608290682Scem
1609289542Scem	/* Initiate PCI-E link training */
1610302484Smav	ntb_link_enable(ntb->device, NTB_SPEED_AUTO, NTB_WIDTH_AUTO);
1611250079Scarl
1612289648Scem	callout_reset(&ntb->heartbeat_timer, 0, atom_link_hb, ntb);
1613289542Scem
1614250079Scarl	return (0);
1615250079Scarl}
1616250079Scarl
1617289648Scem/* XXX: Linux driver doesn't seem to do any of this for Atom. */
1618255279Scarlstatic void
1619289648Scemconfigure_atom_secondary_side_bars(struct ntb_softc *ntb)
1620255279Scarl{
1621255279Scarl
1622255279Scarl	if (ntb->dev_type == NTB_DEV_USD) {
1623289648Scem		ntb_reg_write(8, ATOM_PBAR2XLAT_OFFSET,
1624290725Scem		    XEON_B2B_BAR2_ADDR64);
1625289648Scem		ntb_reg_write(8, ATOM_PBAR4XLAT_OFFSET,
1626290725Scem		    XEON_B2B_BAR4_ADDR64);
1627290725Scem		ntb_reg_write(8, ATOM_MBAR23_OFFSET, XEON_B2B_BAR2_ADDR64);
1628290725Scem		ntb_reg_write(8, ATOM_MBAR45_OFFSET, XEON_B2B_BAR4_ADDR64);
1629255279Scarl	} else {
1630289648Scem		ntb_reg_write(8, ATOM_PBAR2XLAT_OFFSET,
1631290725Scem		    XEON_B2B_BAR2_ADDR64);
1632289648Scem		ntb_reg_write(8, ATOM_PBAR4XLAT_OFFSET,
1633290725Scem		    XEON_B2B_BAR4_ADDR64);
1634290725Scem		ntb_reg_write(8, ATOM_MBAR23_OFFSET, XEON_B2B_BAR2_ADDR64);
1635290725Scem		ntb_reg_write(8, ATOM_MBAR45_OFFSET, XEON_B2B_BAR4_ADDR64);
1636255279Scarl	}
1637255279Scarl}
1638255279Scarl
1639289543Scem
1640289543Scem/*
1641289543Scem * When working around Xeon SDOORBELL errata by remapping remote registers in a
1642289543Scem * MW, limit the B2B MW to half a MW.  By sharing a MW, half the shared MW
1643289543Scem * remains for use by a higher layer.
1644289543Scem *
1645289543Scem * Will only be used if working around SDOORBELL errata and the BIOS-configured
1646289543Scem * MW size is sufficiently large.
1647289543Scem */
1648289543Scemstatic unsigned int ntb_b2b_mw_share;
1649289543ScemSYSCTL_UINT(_hw_ntb, OID_AUTO, b2b_mw_share, CTLFLAG_RDTUN, &ntb_b2b_mw_share,
1650289543Scem    0, "If enabled (non-zero), prefer to share half of the B2B peer register "
1651289543Scem    "MW with higher level consumers.  Both sides of the NTB MUST set the same "
1652289543Scem    "value here.");
1653289543Scem
1654289543Scemstatic void
1655289543Scemxeon_reset_sbar_size(struct ntb_softc *ntb, enum ntb_bar idx,
1656289543Scem    enum ntb_bar regbar)
1657289543Scem{
1658289543Scem	struct ntb_pci_bar_info *bar;
1659289543Scem	uint8_t bar_sz;
1660289543Scem
1661302484Smav	if (!HAS_FEATURE(ntb, NTB_SPLIT_BAR) && idx >= NTB_B2B_BAR_3)
1662289543Scem		return;
1663289543Scem
1664289543Scem	bar = &ntb->bar_info[idx];
1665289543Scem	bar_sz = pci_read_config(ntb->device, bar->psz_off, 1);
1666289543Scem	if (idx == regbar) {
1667289543Scem		if (ntb->b2b_off != 0)
1668289543Scem			bar_sz--;
1669289543Scem		else
1670289543Scem			bar_sz = 0;
1671289543Scem	}
1672289543Scem	pci_write_config(ntb->device, bar->ssz_off, bar_sz, 1);
1673289543Scem	bar_sz = pci_read_config(ntb->device, bar->ssz_off, 1);
1674289543Scem	(void)bar_sz;
1675289543Scem}
1676289543Scem
1677289543Scemstatic void
1678289546Scemxeon_set_sbar_base_and_limit(struct ntb_softc *ntb, uint64_t bar_addr,
1679289543Scem    enum ntb_bar idx, enum ntb_bar regbar)
1680289543Scem{
1681301293Smav	uint64_t reg_val;
1682289546Scem	uint32_t base_reg, lmt_reg;
1683289543Scem
1684289546Scem	bar_get_xlat_params(ntb, idx, &base_reg, NULL, &lmt_reg);
1685302482Smav	if (idx == regbar) {
1686302482Smav		if (ntb->b2b_off)
1687302482Smav			bar_addr += ntb->b2b_off;
1688302482Smav		else
1689302482Smav			bar_addr = 0;
1690302482Smav	}
1691289543Scem
1692295618Scem	/*
1693295618Scem	 * Set limit registers first to avoid an errata where setting the base
1694295618Scem	 * registers locks the limit registers.
1695295618Scem	 */
1696289546Scem	if (!bar_is_64bit(ntb, idx)) {
1697301293Smav		ntb_reg_write(4, lmt_reg, bar_addr);
1698295618Scem		reg_val = ntb_reg_read(4, lmt_reg);
1699295618Scem		(void)reg_val;
1700295618Scem
1701289546Scem		ntb_reg_write(4, base_reg, bar_addr);
1702289546Scem		reg_val = ntb_reg_read(4, base_reg);
1703289546Scem		(void)reg_val;
1704295618Scem	} else {
1705301293Smav		ntb_reg_write(8, lmt_reg, bar_addr);
1706295618Scem		reg_val = ntb_reg_read(8, lmt_reg);
1707295618Scem		(void)reg_val;
1708289546Scem
1709289546Scem		ntb_reg_write(8, base_reg, bar_addr);
1710289546Scem		reg_val = ntb_reg_read(8, base_reg);
1711289546Scem		(void)reg_val;
1712289543Scem	}
1713289543Scem}
1714289543Scem
1715289543Scemstatic void
1716289543Scemxeon_set_pbar_xlat(struct ntb_softc *ntb, uint64_t base_addr, enum ntb_bar idx)
1717289543Scem{
1718289543Scem	struct ntb_pci_bar_info *bar;
1719289543Scem
1720289543Scem	bar = &ntb->bar_info[idx];
1721302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR) && idx >= NTB_B2B_BAR_2) {
1722289543Scem		ntb_reg_write(4, bar->pbarxlat_off, base_addr);
1723289543Scem		base_addr = ntb_reg_read(4, bar->pbarxlat_off);
1724289543Scem	} else {
1725289543Scem		ntb_reg_write(8, bar->pbarxlat_off, base_addr);
1726289543Scem		base_addr = ntb_reg_read(8, bar->pbarxlat_off);
1727289543Scem	}
1728289543Scem	(void)base_addr;
1729289543Scem}
1730289543Scem
1731289542Scemstatic int
1732289542Scemxeon_setup_b2b_mw(struct ntb_softc *ntb, const struct ntb_b2b_addr *addr,
1733289542Scem    const struct ntb_b2b_addr *peer_addr)
1734255279Scarl{
1735289543Scem	struct ntb_pci_bar_info *b2b_bar;
1736289543Scem	vm_size_t bar_size;
1737289543Scem	uint64_t bar_addr;
1738289543Scem	enum ntb_bar b2b_bar_num, i;
1739255279Scarl
1740289543Scem	if (ntb->b2b_mw_idx == B2B_MW_DISABLED) {
1741289543Scem		b2b_bar = NULL;
1742289543Scem		b2b_bar_num = NTB_CONFIG_BAR;
1743289543Scem		ntb->b2b_off = 0;
1744289543Scem	} else {
1745289543Scem		b2b_bar_num = ntb_mw_to_bar(ntb, ntb->b2b_mw_idx);
1746289543Scem		KASSERT(b2b_bar_num > 0 && b2b_bar_num < NTB_MAX_BARS,
1747289543Scem		    ("invalid b2b mw bar"));
1748289543Scem
1749289543Scem		b2b_bar = &ntb->bar_info[b2b_bar_num];
1750289543Scem		bar_size = b2b_bar->size;
1751289543Scem
1752289543Scem		if (ntb_b2b_mw_share != 0 &&
1753289543Scem		    (bar_size >> 1) >= XEON_B2B_MIN_SIZE)
1754289543Scem			ntb->b2b_off = bar_size >> 1;
1755289543Scem		else if (bar_size >= XEON_B2B_MIN_SIZE) {
1756289543Scem			ntb->b2b_off = 0;
1757289543Scem		} else {
1758289543Scem			device_printf(ntb->device,
1759289543Scem			    "B2B bar size is too small!\n");
1760289543Scem			return (EIO);
1761289543Scem		}
1762255279Scarl	}
1763289542Scem
1764289543Scem	/*
1765289543Scem	 * Reset the secondary bar sizes to match the primary bar sizes.
1766289543Scem	 * (Except, disable or halve the size of the B2B secondary bar.)
1767289543Scem	 */
1768289543Scem	for (i = NTB_B2B_BAR_1; i < NTB_MAX_BARS; i++)
1769289543Scem		xeon_reset_sbar_size(ntb, i, b2b_bar_num);
1770289543Scem
1771289543Scem	bar_addr = 0;
1772289543Scem	if (b2b_bar_num == NTB_CONFIG_BAR)
1773289543Scem		bar_addr = addr->bar0_addr;
1774289543Scem	else if (b2b_bar_num == NTB_B2B_BAR_1)
1775289543Scem		bar_addr = addr->bar2_addr64;
1776302484Smav	else if (b2b_bar_num == NTB_B2B_BAR_2 && !HAS_FEATURE(ntb, NTB_SPLIT_BAR))
1777289543Scem		bar_addr = addr->bar4_addr64;
1778289543Scem	else if (b2b_bar_num == NTB_B2B_BAR_2)
1779289543Scem		bar_addr = addr->bar4_addr32;
1780289543Scem	else if (b2b_bar_num == NTB_B2B_BAR_3)
1781289543Scem		bar_addr = addr->bar5_addr32;
1782289543Scem	else
1783289543Scem		KASSERT(false, ("invalid bar"));
1784289543Scem
1785289543Scem	ntb_reg_write(8, XEON_SBAR0BASE_OFFSET, bar_addr);
1786289543Scem
1787289543Scem	/*
1788289543Scem	 * Other SBARs are normally hit by the PBAR xlat, except for the b2b
1789289543Scem	 * register BAR.  The B2B BAR is either disabled above or configured
1790289543Scem	 * half-size.  It starts at PBAR xlat + offset.
1791289543Scem	 *
1792289543Scem	 * Also set up incoming BAR limits == base (zero length window).
1793289543Scem	 */
1794289543Scem	xeon_set_sbar_base_and_limit(ntb, addr->bar2_addr64, NTB_B2B_BAR_1,
1795289543Scem	    b2b_bar_num);
1796302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR)) {
1797289543Scem		xeon_set_sbar_base_and_limit(ntb, addr->bar4_addr32,
1798289543Scem		    NTB_B2B_BAR_2, b2b_bar_num);
1799289543Scem		xeon_set_sbar_base_and_limit(ntb, addr->bar5_addr32,
1800289543Scem		    NTB_B2B_BAR_3, b2b_bar_num);
1801289542Scem	} else
1802289543Scem		xeon_set_sbar_base_and_limit(ntb, addr->bar4_addr64,
1803289543Scem		    NTB_B2B_BAR_2, b2b_bar_num);
1804289543Scem
1805289543Scem	/* Zero incoming translation addrs */
1806289543Scem	ntb_reg_write(8, XEON_SBAR2XLAT_OFFSET, 0);
1807289543Scem	ntb_reg_write(8, XEON_SBAR4XLAT_OFFSET, 0);
1808289543Scem
1809302484Smav	if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP)) {
1810295618Scem		size_t size, xlatoffset;
1811302493Smav		enum ntb_bar bar_num;
1812295618Scem
1813302493Smav		bar_num = ntb_mw_to_bar(ntb, ntb->msix_mw_idx);
1814302493Smav		switch (bar_num) {
1815295618Scem		case NTB_B2B_BAR_1:
1816295618Scem			size = 8;
1817295618Scem			xlatoffset = XEON_SBAR2XLAT_OFFSET;
1818295618Scem			break;
1819295618Scem		case NTB_B2B_BAR_2:
1820295618Scem			xlatoffset = XEON_SBAR4XLAT_OFFSET;
1821302484Smav			if (HAS_FEATURE(ntb, NTB_SPLIT_BAR))
1822295618Scem				size = 4;
1823295618Scem			else
1824295618Scem				size = 8;
1825295618Scem			break;
1826295618Scem		case NTB_B2B_BAR_3:
1827295618Scem			xlatoffset = XEON_SBAR5XLAT_OFFSET;
1828295618Scem			size = 4;
1829295618Scem			break;
1830295618Scem		default:
1831295618Scem			KASSERT(false, ("Bogus msix mw idx: %u",
1832295618Scem			    ntb->msix_mw_idx));
1833295618Scem			return (EINVAL);
1834295618Scem		}
1835295618Scem
1836295618Scem		/*
1837295618Scem		 * We point the chosen MSIX MW BAR xlat to remote LAPIC for
1838295618Scem		 * workaround
1839295618Scem		 */
1840301293Smav		if (size == 4) {
1841295618Scem			ntb_reg_write(4, xlatoffset, MSI_INTEL_ADDR_BASE);
1842301293Smav			ntb->msix_xlat = ntb_reg_read(4, xlatoffset);
1843301293Smav		} else {
1844295618Scem			ntb_reg_write(8, xlatoffset, MSI_INTEL_ADDR_BASE);
1845301293Smav			ntb->msix_xlat = ntb_reg_read(8, xlatoffset);
1846301293Smav		}
1847302493Smav
1848302493Smav		ntb->peer_lapic_bar =  &ntb->bar_info[bar_num];
1849295618Scem	}
1850295618Scem	(void)ntb_reg_read(8, XEON_SBAR2XLAT_OFFSET);
1851295618Scem	(void)ntb_reg_read(8, XEON_SBAR4XLAT_OFFSET);
1852295618Scem
1853289543Scem	/* Zero outgoing translation limits (whole bar size windows) */
1854289543Scem	ntb_reg_write(8, XEON_PBAR2LMT_OFFSET, 0);
1855289543Scem	ntb_reg_write(8, XEON_PBAR4LMT_OFFSET, 0);
1856289543Scem
1857289543Scem	/* Set outgoing translation offsets */
1858289543Scem	xeon_set_pbar_xlat(ntb, peer_addr->bar2_addr64, NTB_B2B_BAR_1);
1859302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR)) {
1860289543Scem		xeon_set_pbar_xlat(ntb, peer_addr->bar4_addr32, NTB_B2B_BAR_2);
1861289543Scem		xeon_set_pbar_xlat(ntb, peer_addr->bar5_addr32, NTB_B2B_BAR_3);
1862289543Scem	} else
1863289543Scem		xeon_set_pbar_xlat(ntb, peer_addr->bar4_addr64, NTB_B2B_BAR_2);
1864289543Scem
1865289543Scem	/* Set the translation offset for B2B registers */
1866289543Scem	bar_addr = 0;
1867289543Scem	if (b2b_bar_num == NTB_CONFIG_BAR)
1868289543Scem		bar_addr = peer_addr->bar0_addr;
1869289543Scem	else if (b2b_bar_num == NTB_B2B_BAR_1)
1870289543Scem		bar_addr = peer_addr->bar2_addr64;
1871302484Smav	else if (b2b_bar_num == NTB_B2B_BAR_2 && !HAS_FEATURE(ntb, NTB_SPLIT_BAR))
1872289543Scem		bar_addr = peer_addr->bar4_addr64;
1873289543Scem	else if (b2b_bar_num == NTB_B2B_BAR_2)
1874289543Scem		bar_addr = peer_addr->bar4_addr32;
1875289543Scem	else if (b2b_bar_num == NTB_B2B_BAR_3)
1876289543Scem		bar_addr = peer_addr->bar5_addr32;
1877289543Scem	else
1878289543Scem		KASSERT(false, ("invalid bar"));
1879289543Scem
1880289543Scem	/*
1881289543Scem	 * B2B_XLAT_OFFSET is a 64-bit register but can only be written 32 bits
1882289543Scem	 * at a time.
1883289543Scem	 */
1884289543Scem	ntb_reg_write(4, XEON_B2B_XLAT_OFFSETL, bar_addr & 0xffffffff);
1885289543Scem	ntb_reg_write(4, XEON_B2B_XLAT_OFFSETU, bar_addr >> 32);
1886289542Scem	return (0);
1887255279Scarl}
1888255279Scarl
1889289546Scemstatic inline bool
1890295618Scem_xeon_link_is_up(struct ntb_softc *ntb)
1891295618Scem{
1892295618Scem
1893295618Scem	if (ntb->conn_type == NTB_CONN_TRANSPARENT)
1894295618Scem		return (true);
1895295618Scem	return ((ntb->lnk_sta & NTB_LINK_STATUS_ACTIVE) != 0);
1896295618Scem}
1897295618Scem
1898295618Scemstatic inline bool
1899289546Scemlink_is_up(struct ntb_softc *ntb)
1900289546Scem{
1901289546Scem
1902295618Scem	if (ntb->type == NTB_XEON)
1903295618Scem		return (_xeon_link_is_up(ntb) && (ntb->peer_msix_good ||
1904302484Smav		    !HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP)));
1905289546Scem
1906289648Scem	KASSERT(ntb->type == NTB_ATOM, ("ntb type"));
1907289648Scem	return ((ntb->ntb_ctl & ATOM_CNTL_LINK_DOWN) == 0);
1908289546Scem}
1909289546Scem
1910289546Scemstatic inline bool
1911289648Scematom_link_is_err(struct ntb_softc *ntb)
1912289546Scem{
1913289546Scem	uint32_t status;
1914289546Scem
1915289648Scem	KASSERT(ntb->type == NTB_ATOM, ("ntb type"));
1916289546Scem
1917289648Scem	status = ntb_reg_read(4, ATOM_LTSSMSTATEJMP_OFFSET);
1918289648Scem	if ((status & ATOM_LTSSMSTATEJMP_FORCEDETECT) != 0)
1919289546Scem		return (true);
1920289546Scem
1921289648Scem	status = ntb_reg_read(4, ATOM_IBSTERRRCRVSTS0_OFFSET);
1922289648Scem	return ((status & ATOM_IBIST_ERR_OFLOW) != 0);
1923289546Scem}
1924289546Scem
1925289648Scem/* Atom does not have link status interrupt, poll on that platform */
1926250079Scarlstatic void
1927289648Scematom_link_hb(void *arg)
1928250079Scarl{
1929250079Scarl	struct ntb_softc *ntb = arg;
1930289546Scem	sbintime_t timo, poll_ts;
1931250079Scarl
1932289546Scem	timo = NTB_HB_TIMEOUT * hz;
1933289546Scem	poll_ts = ntb->last_ts + timo;
1934289546Scem
1935289542Scem	/*
1936289542Scem	 * Delay polling the link status if an interrupt was received, unless
1937289542Scem	 * the cached link status says the link is down.
1938289542Scem	 */
1939289546Scem	if ((sbintime_t)ticks - poll_ts < 0 && link_is_up(ntb)) {
1940289546Scem		timo = poll_ts - ticks;
1941289542Scem		goto out;
1942289546Scem	}
1943289542Scem
1944289546Scem	if (ntb_poll_link(ntb))
1945302484Smav		ntb_link_event(ntb->device);
1946289542Scem
1947289648Scem	if (!link_is_up(ntb) && atom_link_is_err(ntb)) {
1948289546Scem		/* Link is down with error, proceed with recovery */
1949289648Scem		callout_reset(&ntb->lr_timer, 0, recover_atom_link, ntb);
1950289546Scem		return;
1951250079Scarl	}
1952250079Scarl
1953289542Scemout:
1954289648Scem	callout_reset(&ntb->heartbeat_timer, timo, atom_link_hb, ntb);
1955250079Scarl}
1956250079Scarl
1957250079Scarlstatic void
1958289648Scematom_perform_link_restart(struct ntb_softc *ntb)
1959250079Scarl{
1960250079Scarl	uint32_t status;
1961250079Scarl
1962250079Scarl	/* Driver resets the NTB ModPhy lanes - magic! */
1963289648Scem	ntb_reg_write(1, ATOM_MODPHY_PCSREG6, 0xe0);
1964289648Scem	ntb_reg_write(1, ATOM_MODPHY_PCSREG4, 0x40);
1965289648Scem	ntb_reg_write(1, ATOM_MODPHY_PCSREG4, 0x60);
1966289648Scem	ntb_reg_write(1, ATOM_MODPHY_PCSREG6, 0x60);
1967250079Scarl
1968250079Scarl	/* Driver waits 100ms to allow the NTB ModPhy to settle */
1969250079Scarl	pause("ModPhy", hz / 10);
1970250079Scarl
1971250079Scarl	/* Clear AER Errors, write to clear */
1972289648Scem	status = ntb_reg_read(4, ATOM_ERRCORSTS_OFFSET);
1973250079Scarl	status &= PCIM_AER_COR_REPLAY_ROLLOVER;
1974289648Scem	ntb_reg_write(4, ATOM_ERRCORSTS_OFFSET, status);
1975250079Scarl
1976250079Scarl	/* Clear unexpected electrical idle event in LTSSM, write to clear */
1977289648Scem	status = ntb_reg_read(4, ATOM_LTSSMERRSTS0_OFFSET);
1978289648Scem	status |= ATOM_LTSSMERRSTS0_UNEXPECTEDEI;
1979289648Scem	ntb_reg_write(4, ATOM_LTSSMERRSTS0_OFFSET, status);
1980250079Scarl
1981250079Scarl	/* Clear DeSkew Buffer error, write to clear */
1982289648Scem	status = ntb_reg_read(4, ATOM_DESKEWSTS_OFFSET);
1983289648Scem	status |= ATOM_DESKEWSTS_DBERR;
1984289648Scem	ntb_reg_write(4, ATOM_DESKEWSTS_OFFSET, status);
1985250079Scarl
1986289648Scem	status = ntb_reg_read(4, ATOM_IBSTERRRCRVSTS0_OFFSET);
1987289648Scem	status &= ATOM_IBIST_ERR_OFLOW;
1988289648Scem	ntb_reg_write(4, ATOM_IBSTERRRCRVSTS0_OFFSET, status);
1989250079Scarl
1990250079Scarl	/* Releases the NTB state machine to allow the link to retrain */
1991289648Scem	status = ntb_reg_read(4, ATOM_LTSSMSTATEJMP_OFFSET);
1992289648Scem	status &= ~ATOM_LTSSMSTATEJMP_FORCEDETECT;
1993289648Scem	ntb_reg_write(4, ATOM_LTSSMSTATEJMP_OFFSET, status);
1994250079Scarl}
1995250079Scarl
1996302484Smavstatic int
1997302484Smavntb_set_ctx(device_t dev, void *ctx, const struct ntb_ctx_ops *ops)
1998250079Scarl{
1999302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2000250079Scarl
2001289546Scem	if (ctx == NULL || ops == NULL)
2002289546Scem		return (EINVAL);
2003250079Scarl
2004302491Smav	rm_wlock(&ntb->ctx_lock);
2005289546Scem	if (ntb->ctx_ops != NULL) {
2006302491Smav		rm_wunlock(&ntb->ctx_lock);
2007289546Scem		return (EINVAL);
2008250079Scarl	}
2009289546Scem	ntb->ntb_ctx = ctx;
2010289546Scem	ntb->ctx_ops = ops;
2011302491Smav	rm_wunlock(&ntb->ctx_lock);
2012250079Scarl
2013289546Scem	return (0);
2014250079Scarl}
2015250079Scarl
2016289546Scem/*
2017289546Scem * It is expected that this will only be used from contexts where the ctx_lock
2018289546Scem * is not needed to protect ntb_ctx lifetime.
2019289546Scem */
2020302484Smavstatic void *
2021302484Smavntb_get_ctx(device_t dev, const struct ntb_ctx_ops **ops)
2022289546Scem{
2023302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2024289546Scem
2025289546Scem	KASSERT(ntb->ntb_ctx != NULL && ntb->ctx_ops != NULL, ("bogus"));
2026289546Scem	if (ops != NULL)
2027289546Scem		*ops = ntb->ctx_ops;
2028289546Scem	return (ntb->ntb_ctx);
2029289546Scem}
2030289546Scem
2031302484Smavstatic void
2032302484Smavntb_clear_ctx(device_t dev)
2033289546Scem{
2034302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2035289546Scem
2036302491Smav	rm_wlock(&ntb->ctx_lock);
2037289546Scem	ntb->ntb_ctx = NULL;
2038289546Scem	ntb->ctx_ops = NULL;
2039302491Smav	rm_wunlock(&ntb->ctx_lock);
2040289546Scem}
2041289546Scem
2042289546Scem/*
2043289546Scem * ntb_link_event() - notify driver context of a change in link status
2044289546Scem * @ntb:        NTB device context
2045289546Scem *
2046289546Scem * Notify the driver context that the link status may have changed.  The driver
2047289546Scem * should call ntb_link_is_up() to get the current status.
2048289546Scem */
2049302484Smavstatic void
2050302484Smavntb_link_event(device_t dev)
2051289546Scem{
2052302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2053302491Smav	struct rm_priotracker ctx_tracker;
2054289546Scem
2055302491Smav	rm_rlock(&ntb->ctx_lock, &ctx_tracker);
2056289546Scem	if (ntb->ctx_ops != NULL && ntb->ctx_ops->link_event != NULL)
2057289546Scem		ntb->ctx_ops->link_event(ntb->ntb_ctx);
2058302491Smav	rm_runlock(&ntb->ctx_lock, &ctx_tracker);
2059289546Scem}
2060289546Scem
2061289546Scem/*
2062289546Scem * ntb_db_event() - notify driver context of a doorbell event
2063289546Scem * @ntb:        NTB device context
2064289546Scem * @vector:     Interrupt vector number
2065289546Scem *
2066289546Scem * Notify the driver context of a doorbell event.  If hardware supports
2067289546Scem * multiple interrupt vectors for doorbells, the vector number indicates which
2068289546Scem * vector received the interrupt.  The vector number is relative to the first
2069289546Scem * vector used for doorbells, starting at zero, and must be less than
2070289546Scem * ntb_db_vector_count().  The driver may call ntb_db_read() to check which
2071289546Scem * doorbell bits need service, and ntb_db_vector_mask() to determine which of
2072289546Scem * those bits are associated with the vector number.
2073289546Scem */
2074250079Scarlstatic void
2075302484Smavntb_db_event(device_t dev, uint32_t vec)
2076289272Scem{
2077302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2078302491Smav	struct rm_priotracker ctx_tracker;
2079289546Scem
2080302491Smav	rm_rlock(&ntb->ctx_lock, &ctx_tracker);
2081289546Scem	if (ntb->ctx_ops != NULL && ntb->ctx_ops->db_event != NULL)
2082289546Scem		ntb->ctx_ops->db_event(ntb->ntb_ctx, vec);
2083302491Smav	rm_runlock(&ntb->ctx_lock, &ctx_tracker);
2084289546Scem}
2085289546Scem
2086302484Smavstatic int
2087302484Smavntb_link_enable(device_t dev, enum ntb_speed speed __unused,
2088302484Smav    enum ntb_width width __unused)
2089289546Scem{
2090302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2091289280Scem	uint32_t cntl;
2092289272Scem
2093300100Scem	ntb_printf(2, "%s\n", __func__);
2094300100Scem
2095289648Scem	if (ntb->type == NTB_ATOM) {
2096289542Scem		pci_write_config(ntb->device, NTB_PPD_OFFSET,
2097289648Scem		    ntb->ppd | ATOM_PPD_INIT_LINK, 4);
2098289546Scem		return (0);
2099289542Scem	}
2100289542Scem
2101289280Scem	if (ntb->conn_type == NTB_CONN_TRANSPARENT) {
2102302484Smav		ntb_link_event(dev);
2103289546Scem		return (0);
2104289280Scem	}
2105289280Scem
2106289542Scem	cntl = ntb_reg_read(4, ntb->reg->ntb_ctl);
2107289280Scem	cntl &= ~(NTB_CNTL_LINK_DISABLE | NTB_CNTL_CFG_LOCK);
2108289280Scem	cntl |= NTB_CNTL_P2S_BAR23_SNOOP | NTB_CNTL_S2P_BAR23_SNOOP;
2109289397Scem	cntl |= NTB_CNTL_P2S_BAR4_SNOOP | NTB_CNTL_S2P_BAR4_SNOOP;
2110302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR))
2111289397Scem		cntl |= NTB_CNTL_P2S_BAR5_SNOOP | NTB_CNTL_S2P_BAR5_SNOOP;
2112289542Scem	ntb_reg_write(4, ntb->reg->ntb_ctl, cntl);
2113289546Scem	return (0);
2114289272Scem}
2115289272Scem
2116302484Smavstatic int
2117302484Smavntb_link_disable(device_t dev)
2118289272Scem{
2119302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2120289272Scem	uint32_t cntl;
2121289272Scem
2122300100Scem	ntb_printf(2, "%s\n", __func__);
2123300100Scem
2124289272Scem	if (ntb->conn_type == NTB_CONN_TRANSPARENT) {
2125302484Smav		ntb_link_event(dev);
2126289546Scem		return (0);
2127289272Scem	}
2128289272Scem
2129289542Scem	cntl = ntb_reg_read(4, ntb->reg->ntb_ctl);
2130289280Scem	cntl &= ~(NTB_CNTL_P2S_BAR23_SNOOP | NTB_CNTL_S2P_BAR23_SNOOP);
2131289397Scem	cntl &= ~(NTB_CNTL_P2S_BAR4_SNOOP | NTB_CNTL_S2P_BAR4_SNOOP);
2132302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR))
2133289397Scem		cntl &= ~(NTB_CNTL_P2S_BAR5_SNOOP | NTB_CNTL_S2P_BAR5_SNOOP);
2134289280Scem	cntl |= NTB_CNTL_LINK_DISABLE | NTB_CNTL_CFG_LOCK;
2135289542Scem	ntb_reg_write(4, ntb->reg->ntb_ctl, cntl);
2136289546Scem	return (0);
2137289272Scem}
2138289272Scem
2139302484Smavstatic bool
2140302484Smavntb_link_enabled(device_t dev)
2141300100Scem{
2142302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2143300100Scem	uint32_t cntl;
2144300100Scem
2145300100Scem	if (ntb->type == NTB_ATOM) {
2146300100Scem		cntl = pci_read_config(ntb->device, NTB_PPD_OFFSET, 4);
2147300100Scem		return ((cntl & ATOM_PPD_INIT_LINK) != 0);
2148300100Scem	}
2149300100Scem
2150300100Scem	if (ntb->conn_type == NTB_CONN_TRANSPARENT)
2151300100Scem		return (true);
2152300100Scem
2153300100Scem	cntl = ntb_reg_read(4, ntb->reg->ntb_ctl);
2154300100Scem	return ((cntl & NTB_CNTL_LINK_DISABLE) == 0);
2155300100Scem}
2156300100Scem
2157289272Scemstatic void
2158289648Scemrecover_atom_link(void *arg)
2159250079Scarl{
2160250079Scarl	struct ntb_softc *ntb = arg;
2161289608Scem	unsigned speed, width, oldspeed, oldwidth;
2162250079Scarl	uint32_t status32;
2163250079Scarl
2164289648Scem	atom_perform_link_restart(ntb);
2165250079Scarl
2166289232Scem	/*
2167289232Scem	 * There is a potential race between the 2 NTB devices recovering at
2168289232Scem	 * the same time.  If the times are the same, the link will not recover
2169289232Scem	 * and the driver will be stuck in this loop forever.  Add a random
2170289232Scem	 * interval to the recovery time to prevent this race.
2171289232Scem	 */
2172289648Scem	status32 = arc4random() % ATOM_LINK_RECOVERY_TIME;
2173289648Scem	pause("Link", (ATOM_LINK_RECOVERY_TIME + status32) * hz / 1000);
2174289232Scem
2175289648Scem	if (atom_link_is_err(ntb))
2176250079Scarl		goto retry;
2177250079Scarl
2178289542Scem	status32 = ntb_reg_read(4, ntb->reg->ntb_ctl);
2179289648Scem	if ((status32 & ATOM_CNTL_LINK_DOWN) != 0)
2180289232Scem		goto out;
2181289232Scem
2182289542Scem	status32 = ntb_reg_read(4, ntb->reg->lnk_sta);
2183289608Scem	width = NTB_LNK_STA_WIDTH(status32);
2184289608Scem	speed = status32 & NTB_LINK_SPEED_MASK;
2185289608Scem
2186289608Scem	oldwidth = NTB_LNK_STA_WIDTH(ntb->lnk_sta);
2187289608Scem	oldspeed = ntb->lnk_sta & NTB_LINK_SPEED_MASK;
2188289608Scem	if (oldwidth != width || oldspeed != speed)
2189250079Scarl		goto retry;
2190250079Scarl
2191289232Scemout:
2192289648Scem	callout_reset(&ntb->heartbeat_timer, NTB_HB_TIMEOUT * hz, atom_link_hb,
2193289542Scem	    ntb);
2194250079Scarl	return;
2195250079Scarl
2196250079Scarlretry:
2197289648Scem	callout_reset(&ntb->lr_timer, NTB_HB_TIMEOUT * hz, recover_atom_link,
2198250079Scarl	    ntb);
2199250079Scarl}
2200250079Scarl
2201289546Scem/*
2202289546Scem * Polls the HW link status register(s); returns true if something has changed.
2203289546Scem */
2204289546Scemstatic bool
2205289542Scemntb_poll_link(struct ntb_softc *ntb)
2206250079Scarl{
2207250079Scarl	uint32_t ntb_cntl;
2208289546Scem	uint16_t reg_val;
2209250079Scarl
2210289648Scem	if (ntb->type == NTB_ATOM) {
2211289542Scem		ntb_cntl = ntb_reg_read(4, ntb->reg->ntb_ctl);
2212289546Scem		if (ntb_cntl == ntb->ntb_ctl)
2213289546Scem			return (false);
2214289546Scem
2215289542Scem		ntb->ntb_ctl = ntb_cntl;
2216289542Scem		ntb->lnk_sta = ntb_reg_read(4, ntb->reg->lnk_sta);
2217250079Scarl	} else {
2218290678Scem		db_iowrite_raw(ntb, ntb->self_reg->db_bell, ntb->db_link_mask);
2219250079Scarl
2220289546Scem		reg_val = pci_read_config(ntb->device, ntb->reg->lnk_sta, 2);
2221289546Scem		if (reg_val == ntb->lnk_sta)
2222289546Scem			return (false);
2223250079Scarl
2224289546Scem		ntb->lnk_sta = reg_val;
2225295618Scem
2226302484Smav		if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP)) {
2227295618Scem			if (_xeon_link_is_up(ntb)) {
2228295618Scem				if (!ntb->peer_msix_good) {
2229295618Scem					callout_reset(&ntb->peer_msix_work, 0,
2230295618Scem					    ntb_exchange_msix, ntb);
2231295618Scem					return (false);
2232295618Scem				}
2233295618Scem			} else {
2234295618Scem				ntb->peer_msix_good = false;
2235295618Scem				ntb->peer_msix_done = false;
2236295618Scem			}
2237295618Scem		}
2238289542Scem	}
2239289546Scem	return (true);
2240289542Scem}
2241289542Scem
2242289546Scemstatic inline enum ntb_speed
2243289546Scemntb_link_sta_speed(struct ntb_softc *ntb)
2244250079Scarl{
2245250079Scarl
2246289546Scem	if (!link_is_up(ntb))
2247289546Scem		return (NTB_SPEED_NONE);
2248289546Scem	return (ntb->lnk_sta & NTB_LINK_SPEED_MASK);
2249250079Scarl}
2250250079Scarl
2251289546Scemstatic inline enum ntb_width
2252289546Scemntb_link_sta_width(struct ntb_softc *ntb)
2253250079Scarl{
2254250079Scarl
2255289546Scem	if (!link_is_up(ntb))
2256289546Scem		return (NTB_WIDTH_NONE);
2257289546Scem	return (NTB_LNK_STA_WIDTH(ntb->lnk_sta));
2258250079Scarl}
2259250079Scarl
2260289774ScemSYSCTL_NODE(_hw_ntb, OID_AUTO, debug_info, CTLFLAG_RW, 0,
2261289774Scem    "Driver state, statistics, and HW registers");
2262289774Scem
2263289774Scem#define NTB_REGSZ_MASK	(3ul << 30)
2264289774Scem#define NTB_REG_64	(1ul << 30)
2265289774Scem#define NTB_REG_32	(2ul << 30)
2266289774Scem#define NTB_REG_16	(3ul << 30)
2267289774Scem#define NTB_REG_8	(0ul << 30)
2268289774Scem
2269289774Scem#define NTB_DB_READ	(1ul << 29)
2270289774Scem#define NTB_PCI_REG	(1ul << 28)
2271289774Scem#define NTB_REGFLAGS_MASK	(NTB_REGSZ_MASK | NTB_DB_READ | NTB_PCI_REG)
2272289774Scem
2273289774Scemstatic void
2274289774Scemntb_sysctl_init(struct ntb_softc *ntb)
2275289774Scem{
2276300100Scem	struct sysctl_oid_list *globals, *tree_par, *regpar, *statpar, *errpar;
2277289774Scem	struct sysctl_ctx_list *ctx;
2278289774Scem	struct sysctl_oid *tree, *tmptree;
2279289774Scem
2280289774Scem	ctx = device_get_sysctl_ctx(ntb->device);
2281300100Scem	globals = SYSCTL_CHILDREN(device_get_sysctl_tree(ntb->device));
2282289774Scem
2283300100Scem	SYSCTL_ADD_PROC(ctx, globals, OID_AUTO, "link_status",
2284300100Scem	    CTLFLAG_RD | CTLTYPE_STRING, ntb, 0,
2285300100Scem	    sysctl_handle_link_status_human, "A",
2286300100Scem	    "Link status (human readable)");
2287300100Scem	SYSCTL_ADD_PROC(ctx, globals, OID_AUTO, "active",
2288300100Scem	    CTLFLAG_RD | CTLTYPE_UINT, ntb, 0, sysctl_handle_link_status,
2289300100Scem	    "IU", "Link status (1=active, 0=inactive)");
2290300100Scem	SYSCTL_ADD_PROC(ctx, globals, OID_AUTO, "admin_up",
2291300100Scem	    CTLFLAG_RW | CTLTYPE_UINT, ntb, 0, sysctl_handle_link_admin,
2292300100Scem	    "IU", "Set/get interface status (1=UP, 0=DOWN)");
2293300100Scem
2294300100Scem	tree = SYSCTL_ADD_NODE(ctx, globals, OID_AUTO, "debug_info",
2295300100Scem	    CTLFLAG_RD, NULL, "Driver state, statistics, and HW registers");
2296289774Scem	tree_par = SYSCTL_CHILDREN(tree);
2297289774Scem
2298289774Scem	SYSCTL_ADD_UINT(ctx, tree_par, OID_AUTO, "conn_type", CTLFLAG_RD,
2299289774Scem	    &ntb->conn_type, 0, "0 - Transparent; 1 - B2B; 2 - Root Port");
2300289774Scem	SYSCTL_ADD_UINT(ctx, tree_par, OID_AUTO, "dev_type", CTLFLAG_RD,
2301289774Scem	    &ntb->dev_type, 0, "0 - USD; 1 - DSD");
2302290687Scem	SYSCTL_ADD_UINT(ctx, tree_par, OID_AUTO, "ppd", CTLFLAG_RD,
2303290687Scem	    &ntb->ppd, 0, "Raw PPD register (cached)");
2304289774Scem
2305289774Scem	if (ntb->b2b_mw_idx != B2B_MW_DISABLED) {
2306289774Scem		SYSCTL_ADD_U8(ctx, tree_par, OID_AUTO, "b2b_idx", CTLFLAG_RD,
2307289774Scem		    &ntb->b2b_mw_idx, 0,
2308289774Scem		    "Index of the MW used for B2B remote register access");
2309289774Scem		SYSCTL_ADD_UQUAD(ctx, tree_par, OID_AUTO, "b2b_off",
2310289774Scem		    CTLFLAG_RD, &ntb->b2b_off,
2311289774Scem		    "If non-zero, offset of B2B register region in shared MW");
2312289774Scem	}
2313289774Scem
2314289774Scem	SYSCTL_ADD_PROC(ctx, tree_par, OID_AUTO, "features",
2315289774Scem	    CTLFLAG_RD | CTLTYPE_STRING, ntb, 0, sysctl_handle_features, "A",
2316289774Scem	    "Features/errata of this NTB device");
2317289774Scem
2318289774Scem	SYSCTL_ADD_UINT(ctx, tree_par, OID_AUTO, "ntb_ctl", CTLFLAG_RD,
2319290686Scem	    __DEVOLATILE(uint32_t *, &ntb->ntb_ctl), 0,
2320290686Scem	    "NTB CTL register (cached)");
2321289774Scem	SYSCTL_ADD_UINT(ctx, tree_par, OID_AUTO, "lnk_sta", CTLFLAG_RD,
2322290686Scem	    __DEVOLATILE(uint32_t *, &ntb->lnk_sta), 0,
2323290686Scem	    "LNK STA register (cached)");
2324289774Scem
2325289774Scem	SYSCTL_ADD_U8(ctx, tree_par, OID_AUTO, "mw_count", CTLFLAG_RD,
2326291263Scem	    &ntb->mw_count, 0, "MW count");
2327289774Scem	SYSCTL_ADD_U8(ctx, tree_par, OID_AUTO, "spad_count", CTLFLAG_RD,
2328289774Scem	    &ntb->spad_count, 0, "Scratchpad count");
2329289774Scem	SYSCTL_ADD_U8(ctx, tree_par, OID_AUTO, "db_count", CTLFLAG_RD,
2330289774Scem	    &ntb->db_count, 0, "Doorbell count");
2331289774Scem	SYSCTL_ADD_U8(ctx, tree_par, OID_AUTO, "db_vec_count", CTLFLAG_RD,
2332289774Scem	    &ntb->db_vec_count, 0, "Doorbell vector count");
2333289774Scem	SYSCTL_ADD_U8(ctx, tree_par, OID_AUTO, "db_vec_shift", CTLFLAG_RD,
2334289774Scem	    &ntb->db_vec_shift, 0, "Doorbell vector shift");
2335289774Scem
2336289774Scem	SYSCTL_ADD_UQUAD(ctx, tree_par, OID_AUTO, "db_valid_mask", CTLFLAG_RD,
2337289774Scem	    &ntb->db_valid_mask, "Doorbell valid mask");
2338289774Scem	SYSCTL_ADD_UQUAD(ctx, tree_par, OID_AUTO, "db_link_mask", CTLFLAG_RD,
2339289774Scem	    &ntb->db_link_mask, "Doorbell link mask");
2340289774Scem	SYSCTL_ADD_UQUAD(ctx, tree_par, OID_AUTO, "db_mask", CTLFLAG_RD,
2341289774Scem	    &ntb->db_mask, "Doorbell mask (cached)");
2342289774Scem
2343289774Scem	tmptree = SYSCTL_ADD_NODE(ctx, tree_par, OID_AUTO, "registers",
2344289774Scem	    CTLFLAG_RD, NULL, "Raw HW registers (big-endian)");
2345289774Scem	regpar = SYSCTL_CHILDREN(tmptree);
2346289774Scem
2347290682Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "ntbcntl",
2348290682Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb, NTB_REG_32 |
2349290682Scem	    ntb->reg->ntb_ctl, sysctl_handle_register, "IU",
2350290682Scem	    "NTB Control register");
2351290682Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "lnkcap",
2352290682Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb, NTB_REG_32 |
2353290682Scem	    0x19c, sysctl_handle_register, "IU",
2354290682Scem	    "NTB Link Capabilities");
2355290682Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "lnkcon",
2356290682Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb, NTB_REG_32 |
2357290682Scem	    0x1a0, sysctl_handle_register, "IU",
2358290682Scem	    "NTB Link Control register");
2359290682Scem
2360289774Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "db_mask",
2361289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2362289774Scem	    NTB_REG_64 | NTB_DB_READ | ntb->self_reg->db_mask,
2363289774Scem	    sysctl_handle_register, "QU", "Doorbell mask register");
2364289774Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "db_bell",
2365289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2366289774Scem	    NTB_REG_64 | NTB_DB_READ | ntb->self_reg->db_bell,
2367289774Scem	    sysctl_handle_register, "QU", "Doorbell register");
2368289774Scem
2369289774Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "incoming_xlat23",
2370289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2371289774Scem	    NTB_REG_64 | ntb->xlat_reg->bar2_xlat,
2372289774Scem	    sysctl_handle_register, "QU", "Incoming XLAT23 register");
2373302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR)) {
2374289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "incoming_xlat4",
2375289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2376289774Scem		    NTB_REG_32 | ntb->xlat_reg->bar4_xlat,
2377289774Scem		    sysctl_handle_register, "IU", "Incoming XLAT4 register");
2378289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "incoming_xlat5",
2379289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2380289774Scem		    NTB_REG_32 | ntb->xlat_reg->bar5_xlat,
2381289774Scem		    sysctl_handle_register, "IU", "Incoming XLAT5 register");
2382289774Scem	} else {
2383289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "incoming_xlat45",
2384289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2385289774Scem		    NTB_REG_64 | ntb->xlat_reg->bar4_xlat,
2386289774Scem		    sysctl_handle_register, "QU", "Incoming XLAT45 register");
2387289774Scem	}
2388289774Scem
2389289774Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "incoming_lmt23",
2390289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2391289774Scem	    NTB_REG_64 | ntb->xlat_reg->bar2_limit,
2392289774Scem	    sysctl_handle_register, "QU", "Incoming LMT23 register");
2393302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR)) {
2394289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "incoming_lmt4",
2395289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2396289774Scem		    NTB_REG_32 | ntb->xlat_reg->bar4_limit,
2397289774Scem		    sysctl_handle_register, "IU", "Incoming LMT4 register");
2398289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "incoming_lmt5",
2399289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2400289774Scem		    NTB_REG_32 | ntb->xlat_reg->bar5_limit,
2401289774Scem		    sysctl_handle_register, "IU", "Incoming LMT5 register");
2402289774Scem	} else {
2403289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "incoming_lmt45",
2404289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2405289774Scem		    NTB_REG_64 | ntb->xlat_reg->bar4_limit,
2406289774Scem		    sysctl_handle_register, "QU", "Incoming LMT45 register");
2407289774Scem	}
2408289774Scem
2409289774Scem	if (ntb->type == NTB_ATOM)
2410289774Scem		return;
2411289774Scem
2412289774Scem	tmptree = SYSCTL_ADD_NODE(ctx, regpar, OID_AUTO, "xeon_stats",
2413289774Scem	    CTLFLAG_RD, NULL, "Xeon HW statistics");
2414289774Scem	statpar = SYSCTL_CHILDREN(tmptree);
2415289774Scem	SYSCTL_ADD_PROC(ctx, statpar, OID_AUTO, "upstream_mem_miss",
2416289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2417289774Scem	    NTB_REG_16 | XEON_USMEMMISS_OFFSET,
2418289774Scem	    sysctl_handle_register, "SU", "Upstream Memory Miss");
2419289774Scem
2420289774Scem	tmptree = SYSCTL_ADD_NODE(ctx, regpar, OID_AUTO, "xeon_hw_err",
2421289774Scem	    CTLFLAG_RD, NULL, "Xeon HW errors");
2422289774Scem	errpar = SYSCTL_CHILDREN(tmptree);
2423289774Scem
2424290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "ppd",
2425289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2426290687Scem	    NTB_REG_8 | NTB_PCI_REG | NTB_PPD_OFFSET,
2427290687Scem	    sysctl_handle_register, "CU", "PPD");
2428290687Scem
2429290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "pbar23_sz",
2430290687Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2431290687Scem	    NTB_REG_8 | NTB_PCI_REG | XEON_PBAR23SZ_OFFSET,
2432290687Scem	    sysctl_handle_register, "CU", "PBAR23 SZ (log2)");
2433290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "pbar4_sz",
2434290687Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2435290687Scem	    NTB_REG_8 | NTB_PCI_REG | XEON_PBAR4SZ_OFFSET,
2436290687Scem	    sysctl_handle_register, "CU", "PBAR4 SZ (log2)");
2437290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "pbar5_sz",
2438290687Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2439290687Scem	    NTB_REG_8 | NTB_PCI_REG | XEON_PBAR5SZ_OFFSET,
2440290687Scem	    sysctl_handle_register, "CU", "PBAR5 SZ (log2)");
2441290687Scem
2442290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "sbar23_sz",
2443290687Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2444290687Scem	    NTB_REG_8 | NTB_PCI_REG | XEON_SBAR23SZ_OFFSET,
2445290687Scem	    sysctl_handle_register, "CU", "SBAR23 SZ (log2)");
2446290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "sbar4_sz",
2447290687Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2448290687Scem	    NTB_REG_8 | NTB_PCI_REG | XEON_SBAR4SZ_OFFSET,
2449290687Scem	    sysctl_handle_register, "CU", "SBAR4 SZ (log2)");
2450290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "sbar5_sz",
2451290687Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2452290687Scem	    NTB_REG_8 | NTB_PCI_REG | XEON_SBAR5SZ_OFFSET,
2453290687Scem	    sysctl_handle_register, "CU", "SBAR5 SZ (log2)");
2454290687Scem
2455290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "devsts",
2456290687Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2457289774Scem	    NTB_REG_16 | NTB_PCI_REG | XEON_DEVSTS_OFFSET,
2458289774Scem	    sysctl_handle_register, "SU", "DEVSTS");
2459290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "lnksts",
2460289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2461289774Scem	    NTB_REG_16 | NTB_PCI_REG | XEON_LINK_STATUS_OFFSET,
2462289774Scem	    sysctl_handle_register, "SU", "LNKSTS");
2463290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "slnksts",
2464290687Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2465290687Scem	    NTB_REG_16 | NTB_PCI_REG | XEON_SLINK_STATUS_OFFSET,
2466290687Scem	    sysctl_handle_register, "SU", "SLNKSTS");
2467290687Scem
2468289774Scem	SYSCTL_ADD_PROC(ctx, errpar, OID_AUTO, "uncerrsts",
2469289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2470289774Scem	    NTB_REG_32 | NTB_PCI_REG | XEON_UNCERRSTS_OFFSET,
2471289774Scem	    sysctl_handle_register, "IU", "UNCERRSTS");
2472289774Scem	SYSCTL_ADD_PROC(ctx, errpar, OID_AUTO, "corerrsts",
2473289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2474289774Scem	    NTB_REG_32 | NTB_PCI_REG | XEON_CORERRSTS_OFFSET,
2475289774Scem	    sysctl_handle_register, "IU", "CORERRSTS");
2476289774Scem
2477289774Scem	if (ntb->conn_type != NTB_CONN_B2B)
2478289774Scem		return;
2479289774Scem
2480289774Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "outgoing_xlat23",
2481289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2482289774Scem	    NTB_REG_64 | ntb->bar_info[NTB_B2B_BAR_1].pbarxlat_off,
2483289774Scem	    sysctl_handle_register, "QU", "Outgoing XLAT23 register");
2484302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR)) {
2485289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "outgoing_xlat4",
2486289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2487289774Scem		    NTB_REG_32 | ntb->bar_info[NTB_B2B_BAR_2].pbarxlat_off,
2488289774Scem		    sysctl_handle_register, "IU", "Outgoing XLAT4 register");
2489289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "outgoing_xlat5",
2490289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2491289774Scem		    NTB_REG_32 | ntb->bar_info[NTB_B2B_BAR_3].pbarxlat_off,
2492289774Scem		    sysctl_handle_register, "IU", "Outgoing XLAT5 register");
2493289774Scem	} else {
2494289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "outgoing_xlat45",
2495289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2496289774Scem		    NTB_REG_64 | ntb->bar_info[NTB_B2B_BAR_2].pbarxlat_off,
2497289774Scem		    sysctl_handle_register, "QU", "Outgoing XLAT45 register");
2498289774Scem	}
2499289774Scem
2500289774Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "outgoing_lmt23",
2501289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2502289774Scem	    NTB_REG_64 | XEON_PBAR2LMT_OFFSET,
2503289774Scem	    sysctl_handle_register, "QU", "Outgoing LMT23 register");
2504302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR)) {
2505289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "outgoing_lmt4",
2506289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2507289774Scem		    NTB_REG_32 | XEON_PBAR4LMT_OFFSET,
2508289774Scem		    sysctl_handle_register, "IU", "Outgoing LMT4 register");
2509289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "outgoing_lmt5",
2510289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2511289774Scem		    NTB_REG_32 | XEON_PBAR5LMT_OFFSET,
2512289774Scem		    sysctl_handle_register, "IU", "Outgoing LMT5 register");
2513289774Scem	} else {
2514289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "outgoing_lmt45",
2515289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2516289774Scem		    NTB_REG_64 | XEON_PBAR4LMT_OFFSET,
2517289774Scem		    sysctl_handle_register, "QU", "Outgoing LMT45 register");
2518289774Scem	}
2519289774Scem
2520289774Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "sbar01_base",
2521289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2522289774Scem	    NTB_REG_64 | ntb->xlat_reg->bar0_base,
2523289774Scem	    sysctl_handle_register, "QU", "Secondary BAR01 base register");
2524289774Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "sbar23_base",
2525289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2526289774Scem	    NTB_REG_64 | ntb->xlat_reg->bar2_base,
2527289774Scem	    sysctl_handle_register, "QU", "Secondary BAR23 base register");
2528302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR)) {
2529289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "sbar4_base",
2530289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2531289774Scem		    NTB_REG_32 | ntb->xlat_reg->bar4_base,
2532289774Scem		    sysctl_handle_register, "IU",
2533289774Scem		    "Secondary BAR4 base register");
2534289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "sbar5_base",
2535289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2536289774Scem		    NTB_REG_32 | ntb->xlat_reg->bar5_base,
2537289774Scem		    sysctl_handle_register, "IU",
2538289774Scem		    "Secondary BAR5 base register");
2539289774Scem	} else {
2540289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "sbar45_base",
2541289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2542289774Scem		    NTB_REG_64 | ntb->xlat_reg->bar4_base,
2543289774Scem		    sysctl_handle_register, "QU",
2544289774Scem		    "Secondary BAR45 base register");
2545289774Scem	}
2546289774Scem}
2547289774Scem
2548289774Scemstatic int
2549289774Scemsysctl_handle_features(SYSCTL_HANDLER_ARGS)
2550289774Scem{
2551302483Smav	struct ntb_softc *ntb = arg1;
2552289774Scem	struct sbuf sb;
2553289774Scem	int error;
2554289774Scem
2555289774Scem	sbuf_new_for_sysctl(&sb, NULL, 256, req);
2556289774Scem
2557289774Scem	sbuf_printf(&sb, "%b", ntb->features, NTB_FEATURES_STR);
2558289774Scem	error = sbuf_finish(&sb);
2559289774Scem	sbuf_delete(&sb);
2560289774Scem
2561289774Scem	if (error || !req->newptr)
2562289774Scem		return (error);
2563289774Scem	return (EINVAL);
2564289774Scem}
2565289774Scem
2566289774Scemstatic int
2567300100Scemsysctl_handle_link_admin(SYSCTL_HANDLER_ARGS)
2568289774Scem{
2569302483Smav	struct ntb_softc *ntb = arg1;
2570300100Scem	unsigned old, new;
2571300100Scem	int error;
2572300100Scem
2573302484Smav	old = ntb_link_enabled(ntb->device);
2574300100Scem
2575300100Scem	error = SYSCTL_OUT(req, &old, sizeof(old));
2576300100Scem	if (error != 0 || req->newptr == NULL)
2577300100Scem		return (error);
2578300100Scem
2579300100Scem	error = SYSCTL_IN(req, &new, sizeof(new));
2580300100Scem	if (error != 0)
2581300100Scem		return (error);
2582300100Scem
2583300100Scem	ntb_printf(0, "Admin set interface state to '%sabled'\n",
2584300100Scem	    (new != 0)? "en" : "dis");
2585300100Scem
2586300100Scem	if (new != 0)
2587302484Smav		error = ntb_link_enable(ntb->device, NTB_SPEED_AUTO, NTB_WIDTH_AUTO);
2588300100Scem	else
2589302484Smav		error = ntb_link_disable(ntb->device);
2590300100Scem	return (error);
2591300100Scem}
2592300100Scem
2593300100Scemstatic int
2594300100Scemsysctl_handle_link_status_human(SYSCTL_HANDLER_ARGS)
2595300100Scem{
2596302483Smav	struct ntb_softc *ntb = arg1;
2597289774Scem	struct sbuf sb;
2598289774Scem	enum ntb_speed speed;
2599289774Scem	enum ntb_width width;
2600289774Scem	int error;
2601289774Scem
2602289774Scem	sbuf_new_for_sysctl(&sb, NULL, 32, req);
2603289774Scem
2604302484Smav	if (ntb_link_is_up(ntb->device, &speed, &width))
2605289774Scem		sbuf_printf(&sb, "up / PCIe Gen %u / Width x%u",
2606289774Scem		    (unsigned)speed, (unsigned)width);
2607289774Scem	else
2608289774Scem		sbuf_printf(&sb, "down");
2609289774Scem
2610289774Scem	error = sbuf_finish(&sb);
2611289774Scem	sbuf_delete(&sb);
2612289774Scem
2613289774Scem	if (error || !req->newptr)
2614289774Scem		return (error);
2615289774Scem	return (EINVAL);
2616289774Scem}
2617289774Scem
2618289774Scemstatic int
2619300100Scemsysctl_handle_link_status(SYSCTL_HANDLER_ARGS)
2620300100Scem{
2621302483Smav	struct ntb_softc *ntb = arg1;
2622300100Scem	unsigned res;
2623300100Scem	int error;
2624300100Scem
2625302484Smav	res = ntb_link_is_up(ntb->device, NULL, NULL);
2626300100Scem
2627300100Scem	error = SYSCTL_OUT(req, &res, sizeof(res));
2628300100Scem	if (error || !req->newptr)
2629300100Scem		return (error);
2630300100Scem	return (EINVAL);
2631300100Scem}
2632300100Scem
2633300100Scemstatic int
2634289774Scemsysctl_handle_register(SYSCTL_HANDLER_ARGS)
2635289774Scem{
2636289774Scem	struct ntb_softc *ntb;
2637289774Scem	const void *outp;
2638289774Scem	uintptr_t sz;
2639289774Scem	uint64_t umv;
2640289774Scem	char be[sizeof(umv)];
2641289774Scem	size_t outsz;
2642289774Scem	uint32_t reg;
2643289774Scem	bool db, pci;
2644289774Scem	int error;
2645289774Scem
2646289774Scem	ntb = arg1;
2647289774Scem	reg = arg2 & ~NTB_REGFLAGS_MASK;
2648289774Scem	sz = arg2 & NTB_REGSZ_MASK;
2649289774Scem	db = (arg2 & NTB_DB_READ) != 0;
2650289774Scem	pci = (arg2 & NTB_PCI_REG) != 0;
2651289774Scem
2652289774Scem	KASSERT(!(db && pci), ("bogus"));
2653289774Scem
2654289774Scem	if (db) {
2655289774Scem		KASSERT(sz == NTB_REG_64, ("bogus"));
2656289774Scem		umv = db_ioread(ntb, reg);
2657289774Scem		outsz = sizeof(uint64_t);
2658289774Scem	} else {
2659289774Scem		switch (sz) {
2660289774Scem		case NTB_REG_64:
2661289774Scem			if (pci)
2662289774Scem				umv = pci_read_config(ntb->device, reg, 8);
2663289774Scem			else
2664289774Scem				umv = ntb_reg_read(8, reg);
2665289774Scem			outsz = sizeof(uint64_t);
2666289774Scem			break;
2667289774Scem		case NTB_REG_32:
2668289774Scem			if (pci)
2669289774Scem				umv = pci_read_config(ntb->device, reg, 4);
2670289774Scem			else
2671289774Scem				umv = ntb_reg_read(4, reg);
2672289774Scem			outsz = sizeof(uint32_t);
2673289774Scem			break;
2674289774Scem		case NTB_REG_16:
2675289774Scem			if (pci)
2676289774Scem				umv = pci_read_config(ntb->device, reg, 2);
2677289774Scem			else
2678289774Scem				umv = ntb_reg_read(2, reg);
2679289774Scem			outsz = sizeof(uint16_t);
2680289774Scem			break;
2681289774Scem		case NTB_REG_8:
2682289774Scem			if (pci)
2683289774Scem				umv = pci_read_config(ntb->device, reg, 1);
2684289774Scem			else
2685289774Scem				umv = ntb_reg_read(1, reg);
2686289774Scem			outsz = sizeof(uint8_t);
2687289774Scem			break;
2688289774Scem		default:
2689289774Scem			panic("bogus");
2690289774Scem			break;
2691289774Scem		}
2692289774Scem	}
2693289774Scem
2694289774Scem	/* Encode bigendian so that sysctl -x is legible. */
2695289774Scem	be64enc(be, umv);
2696289774Scem	outp = ((char *)be) + sizeof(umv) - outsz;
2697289774Scem
2698289774Scem	error = SYSCTL_OUT(req, outp, outsz);
2699289774Scem	if (error || !req->newptr)
2700289774Scem		return (error);
2701289774Scem	return (EINVAL);
2702289774Scem}
2703289774Scem
2704291263Scemstatic unsigned
2705291263Scemntb_user_mw_to_idx(struct ntb_softc *ntb, unsigned uidx)
2706291263Scem{
2707291263Scem
2708295618Scem	if ((ntb->b2b_mw_idx != B2B_MW_DISABLED && ntb->b2b_off == 0 &&
2709295618Scem	    uidx >= ntb->b2b_mw_idx) ||
2710295618Scem	    (ntb->msix_mw_idx != B2B_MW_DISABLED && uidx >= ntb->msix_mw_idx))
2711295618Scem		uidx++;
2712295618Scem	if ((ntb->b2b_mw_idx != B2B_MW_DISABLED && ntb->b2b_off == 0 &&
2713295618Scem	    uidx >= ntb->b2b_mw_idx) &&
2714295618Scem	    (ntb->msix_mw_idx != B2B_MW_DISABLED && uidx >= ntb->msix_mw_idx))
2715295618Scem		uidx++;
2716291263Scem	return (uidx);
2717291263Scem}
2718291263Scem
2719295618Scemstatic void
2720295618Scemntb_exchange_msix(void *ctx)
2721295618Scem{
2722295618Scem	struct ntb_softc *ntb;
2723295618Scem	uint32_t val;
2724295618Scem	unsigned i;
2725295618Scem
2726295618Scem	ntb = ctx;
2727295618Scem
2728301292Smav	if (ntb->peer_msix_good)
2729301292Smav		goto msix_good;
2730295618Scem	if (ntb->peer_msix_done)
2731295618Scem		goto msix_done;
2732295618Scem
2733295618Scem	for (i = 0; i < XEON_NONLINK_DB_MSIX_BITS; i++) {
2734302484Smav		ntb_peer_spad_write(ntb->device, NTB_MSIX_DATA0 + i,
2735295618Scem		    ntb->msix_data[i].nmd_data);
2736302484Smav		ntb_peer_spad_write(ntb->device, NTB_MSIX_OFS0 + i,
2737301293Smav		    ntb->msix_data[i].nmd_ofs - ntb->msix_xlat);
2738295618Scem	}
2739302484Smav	ntb_peer_spad_write(ntb->device, NTB_MSIX_GUARD, NTB_MSIX_VER_GUARD);
2740295618Scem
2741302484Smav	ntb_spad_read(ntb->device, NTB_MSIX_GUARD, &val);
2742295618Scem	if (val != NTB_MSIX_VER_GUARD)
2743295618Scem		goto reschedule;
2744295618Scem
2745295618Scem	for (i = 0; i < XEON_NONLINK_DB_MSIX_BITS; i++) {
2746302484Smav		ntb_spad_read(ntb->device, NTB_MSIX_DATA0 + i, &val);
2747301293Smav		ntb_printf(2, "remote MSIX data(%u): 0x%x\n", i, val);
2748295618Scem		ntb->peer_msix_data[i].nmd_data = val;
2749302484Smav		ntb_spad_read(ntb->device, NTB_MSIX_OFS0 + i, &val);
2750301293Smav		ntb_printf(2, "remote MSIX addr(%u): 0x%x\n", i, val);
2751295618Scem		ntb->peer_msix_data[i].nmd_ofs = val;
2752295618Scem	}
2753295618Scem
2754295618Scem	ntb->peer_msix_done = true;
2755295618Scem
2756295618Scemmsix_done:
2757302484Smav	ntb_peer_spad_write(ntb->device, NTB_MSIX_DONE, NTB_MSIX_RECEIVED);
2758302484Smav	ntb_spad_read(ntb->device, NTB_MSIX_DONE, &val);
2759295618Scem	if (val != NTB_MSIX_RECEIVED)
2760295618Scem		goto reschedule;
2761295618Scem
2762295618Scem	ntb->peer_msix_good = true;
2763301292Smav	/* Give peer time to see our NTB_MSIX_RECEIVED. */
2764301292Smav	goto reschedule;
2765295618Scem
2766301292Smavmsix_good:
2767295618Scem	ntb_poll_link(ntb);
2768302484Smav	ntb_link_event(ntb->device);
2769295618Scem	return;
2770295618Scem
2771295618Scemreschedule:
2772295618Scem	ntb->lnk_sta = pci_read_config(ntb->device, ntb->reg->lnk_sta, 2);
2773301292Smav	if (_xeon_link_is_up(ntb)) {
2774301292Smav		callout_reset(&ntb->peer_msix_work,
2775301292Smav		    hz * (ntb->peer_msix_good ? 2 : 1) / 100,
2776301292Smav		    ntb_exchange_msix, ntb);
2777301292Smav	} else
2778302484Smav		ntb_spad_clear(ntb->device);
2779295618Scem}
2780295618Scem
2781289546Scem/*
2782289546Scem * Public API to the rest of the OS
2783250079Scarl */
2784250079Scarl
2785302484Smavstatic uint8_t
2786302484Smavntb_spad_count(device_t dev)
2787250079Scarl{
2788302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2789250079Scarl
2790289539Scem	return (ntb->spad_count);
2791250079Scarl}
2792250079Scarl
2793302484Smavstatic uint8_t
2794302484Smavntb_mw_count(device_t dev)
2795289396Scem{
2796302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2797295618Scem	uint8_t res;
2798289396Scem
2799295618Scem	res = ntb->mw_count;
2800291263Scem	if (ntb->b2b_mw_idx != B2B_MW_DISABLED && ntb->b2b_off == 0)
2801295618Scem		res--;
2802295618Scem	if (ntb->msix_mw_idx != B2B_MW_DISABLED)
2803295618Scem		res--;
2804295618Scem	return (res);
2805289396Scem}
2806289396Scem
2807302484Smavstatic int
2808302484Smavntb_spad_write(device_t dev, unsigned int idx, uint32_t val)
2809250079Scarl{
2810302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2811250079Scarl
2812289539Scem	if (idx >= ntb->spad_count)
2813250079Scarl		return (EINVAL);
2814250079Scarl
2815289607Scem	ntb_reg_write(4, ntb->self_reg->spad + idx * 4, val);
2816250079Scarl
2817250079Scarl	return (0);
2818250079Scarl}
2819250079Scarl
2820295618Scem/*
2821295618Scem * Zeros the local scratchpad.
2822295618Scem */
2823302484Smavstatic void
2824302484Smavntb_spad_clear(device_t dev)
2825295618Scem{
2826302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2827295618Scem	unsigned i;
2828295618Scem
2829295618Scem	for (i = 0; i < ntb->spad_count; i++)
2830302484Smav		ntb_spad_write(dev, i, 0);
2831295618Scem}
2832295618Scem
2833302484Smavstatic int
2834302484Smavntb_spad_read(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
2841289607Scem	*val = ntb_reg_read(4, ntb->self_reg->spad + idx * 4);
2842250079Scarl
2843250079Scarl	return (0);
2844250079Scarl}
2845250079Scarl
2846302484Smavstatic int
2847302484Smavntb_peer_spad_write(device_t dev, unsigned int idx, uint32_t val)
2848250079Scarl{
2849302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2850250079Scarl
2851289539Scem	if (idx >= ntb->spad_count)
2852250079Scarl		return (EINVAL);
2853250079Scarl
2854302484Smav	if (HAS_FEATURE(ntb, NTB_SDOORBELL_LOCKUP))
2855290682Scem		ntb_mw_write(4, XEON_SPAD_OFFSET + idx * 4, val);
2856255279Scarl	else
2857289542Scem		ntb_reg_write(4, ntb->peer_reg->spad + idx * 4, val);
2858250079Scarl
2859250079Scarl	return (0);
2860250079Scarl}
2861250079Scarl
2862302484Smavstatic int
2863302484Smavntb_peer_spad_read(device_t dev, unsigned int idx, uint32_t *val)
2864250079Scarl{
2865302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2866250079Scarl
2867289539Scem	if (idx >= ntb->spad_count)
2868250079Scarl		return (EINVAL);
2869250079Scarl
2870302484Smav	if (HAS_FEATURE(ntb, NTB_SDOORBELL_LOCKUP))
2871290682Scem		*val = ntb_mw_read(4, XEON_SPAD_OFFSET + idx * 4);
2872255279Scarl	else
2873289542Scem		*val = ntb_reg_read(4, ntb->peer_reg->spad + idx * 4);
2874250079Scarl
2875250079Scarl	return (0);
2876250079Scarl}
2877250079Scarl
2878302484Smavstatic int
2879302484Smavntb_mw_get_range(device_t dev, unsigned mw_idx, vm_paddr_t *base,
2880291033Scem    caddr_t *vbase, size_t *size, size_t *align, size_t *align_size,
2881291033Scem    bus_addr_t *plimit)
2882250079Scarl{
2883302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2884289546Scem	struct ntb_pci_bar_info *bar;
2885291033Scem	bus_addr_t limit;
2886289546Scem	size_t bar_b2b_off;
2887291033Scem	enum ntb_bar bar_num;
2888250079Scarl
2889302484Smav	if (mw_idx >= ntb_mw_count(dev))
2890289546Scem		return (EINVAL);
2891291263Scem	mw_idx = ntb_user_mw_to_idx(ntb, mw_idx);
2892250079Scarl
2893291033Scem	bar_num = ntb_mw_to_bar(ntb, mw_idx);
2894291033Scem	bar = &ntb->bar_info[bar_num];
2895289546Scem	bar_b2b_off = 0;
2896289546Scem	if (mw_idx == ntb->b2b_mw_idx) {
2897289546Scem		KASSERT(ntb->b2b_off != 0,
2898289546Scem		    ("user shouldn't get non-shared b2b mw"));
2899289546Scem		bar_b2b_off = ntb->b2b_off;
2900289546Scem	}
2901250079Scarl
2902291033Scem	if (bar_is_64bit(ntb, bar_num))
2903291033Scem		limit = BUS_SPACE_MAXADDR;
2904291033Scem	else
2905291033Scem		limit = BUS_SPACE_MAXADDR_32BIT;
2906291033Scem
2907289546Scem	if (base != NULL)
2908289546Scem		*base = bar->pbase + bar_b2b_off;
2909289546Scem	if (vbase != NULL)
2910290679Scem		*vbase = bar->vbase + bar_b2b_off;
2911289546Scem	if (size != NULL)
2912289546Scem		*size = bar->size - bar_b2b_off;
2913289546Scem	if (align != NULL)
2914289546Scem		*align = bar->size;
2915289546Scem	if (align_size != NULL)
2916289546Scem		*align_size = 1;
2917291033Scem	if (plimit != NULL)
2918291033Scem		*plimit = limit;
2919289546Scem	return (0);
2920250079Scarl}
2921250079Scarl
2922302484Smavstatic int
2923302484Smavntb_mw_set_trans(device_t dev, unsigned idx, bus_addr_t addr, size_t size)
2924250079Scarl{
2925302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2926289546Scem	struct ntb_pci_bar_info *bar;
2927289546Scem	uint64_t base, limit, reg_val;
2928289546Scem	size_t bar_size, mw_size;
2929289546Scem	uint32_t base_reg, xlat_reg, limit_reg;
2930289546Scem	enum ntb_bar bar_num;
2931250079Scarl
2932302484Smav	if (idx >= ntb_mw_count(dev))
2933289546Scem		return (EINVAL);
2934291263Scem	idx = ntb_user_mw_to_idx(ntb, idx);
2935250079Scarl
2936289546Scem	bar_num = ntb_mw_to_bar(ntb, idx);
2937289546Scem	bar = &ntb->bar_info[bar_num];
2938250079Scarl
2939289546Scem	bar_size = bar->size;
2940289546Scem	if (idx == ntb->b2b_mw_idx)
2941289546Scem		mw_size = bar_size - ntb->b2b_off;
2942289546Scem	else
2943289546Scem		mw_size = bar_size;
2944250079Scarl
2945289546Scem	/* Hardware requires that addr is aligned to bar size */
2946289546Scem	if ((addr & (bar_size - 1)) != 0)
2947289546Scem		return (EINVAL);
2948250079Scarl
2949289546Scem	if (size > mw_size)
2950289546Scem		return (EINVAL);
2951289546Scem
2952289546Scem	bar_get_xlat_params(ntb, bar_num, &base_reg, &xlat_reg, &limit_reg);
2953289546Scem
2954289546Scem	limit = 0;
2955289546Scem	if (bar_is_64bit(ntb, bar_num)) {
2956291032Scem		base = ntb_reg_read(8, base_reg) & BAR_HIGH_MASK;
2957289546Scem
2958289546Scem		if (limit_reg != 0 && size != mw_size)
2959289546Scem			limit = base + size;
2960289546Scem
2961289546Scem		/* Set and verify translation address */
2962289546Scem		ntb_reg_write(8, xlat_reg, addr);
2963291032Scem		reg_val = ntb_reg_read(8, xlat_reg) & BAR_HIGH_MASK;
2964289546Scem		if (reg_val != addr) {
2965289546Scem			ntb_reg_write(8, xlat_reg, 0);
2966289546Scem			return (EIO);
2967289546Scem		}
2968289546Scem
2969289546Scem		/* Set and verify the limit */
2970289546Scem		ntb_reg_write(8, limit_reg, limit);
2971291032Scem		reg_val = ntb_reg_read(8, limit_reg) & BAR_HIGH_MASK;
2972289546Scem		if (reg_val != limit) {
2973289546Scem			ntb_reg_write(8, limit_reg, base);
2974289546Scem			ntb_reg_write(8, xlat_reg, 0);
2975289546Scem			return (EIO);
2976289546Scem		}
2977289546Scem	} else {
2978289546Scem		/* Configure 32-bit (split) BAR MW */
2979289546Scem
2980291029Scem		if ((addr & UINT32_MAX) != addr)
2981291033Scem			return (ERANGE);
2982291029Scem		if (((addr + size) & UINT32_MAX) != (addr + size))
2983291033Scem			return (ERANGE);
2984289546Scem
2985291032Scem		base = ntb_reg_read(4, base_reg) & BAR_HIGH_MASK;
2986289546Scem
2987289546Scem		if (limit_reg != 0 && size != mw_size)
2988289546Scem			limit = base + size;
2989289546Scem
2990289546Scem		/* Set and verify translation address */
2991289546Scem		ntb_reg_write(4, xlat_reg, addr);
2992291032Scem		reg_val = ntb_reg_read(4, xlat_reg) & BAR_HIGH_MASK;
2993289546Scem		if (reg_val != addr) {
2994289546Scem			ntb_reg_write(4, xlat_reg, 0);
2995289546Scem			return (EIO);
2996289546Scem		}
2997289546Scem
2998289546Scem		/* Set and verify the limit */
2999289546Scem		ntb_reg_write(4, limit_reg, limit);
3000291032Scem		reg_val = ntb_reg_read(4, limit_reg) & BAR_HIGH_MASK;
3001289546Scem		if (reg_val != limit) {
3002289546Scem			ntb_reg_write(4, limit_reg, base);
3003289546Scem			ntb_reg_write(4, xlat_reg, 0);
3004289546Scem			return (EIO);
3005289546Scem		}
3006250079Scarl	}
3007289546Scem	return (0);
3008250079Scarl}
3009250079Scarl
3010302484Smavstatic int
3011302484Smavntb_mw_clear_trans(device_t dev, unsigned mw_idx)
3012289596Scem{
3013289596Scem
3014302484Smav	return (ntb_mw_set_trans(dev, mw_idx, 0, 0));
3015289596Scem}
3016289596Scem
3017302484Smavstatic int
3018302484Smavntb_mw_get_wc(device_t dev, unsigned idx, vm_memattr_t *mode)
3019291031Scem{
3020302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
3021291031Scem	struct ntb_pci_bar_info *bar;
3022291031Scem
3023302484Smav	if (idx >= ntb_mw_count(dev))
3024291031Scem		return (EINVAL);
3025291263Scem	idx = ntb_user_mw_to_idx(ntb, idx);
3026291031Scem
3027291031Scem	bar = &ntb->bar_info[ntb_mw_to_bar(ntb, idx)];
3028291280Scem	*mode = bar->map_mode;
3029291031Scem	return (0);
3030291031Scem}
3031291031Scem
3032302484Smavstatic int
3033302484Smavntb_mw_set_wc(device_t dev, unsigned idx, vm_memattr_t mode)
3034291031Scem{
3035302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
3036291263Scem
3037302484Smav	if (idx >= ntb_mw_count(dev))
3038291263Scem		return (EINVAL);
3039291263Scem
3040291263Scem	idx = ntb_user_mw_to_idx(ntb, idx);
3041291280Scem	return (ntb_mw_set_wc_internal(ntb, idx, mode));
3042291263Scem}
3043291263Scem
3044291263Scemstatic int
3045291280Scemntb_mw_set_wc_internal(struct ntb_softc *ntb, unsigned idx, vm_memattr_t mode)
3046291263Scem{
3047291031Scem	struct ntb_pci_bar_info *bar;
3048291031Scem	int rc;
3049291031Scem
3050291031Scem	bar = &ntb->bar_info[ntb_mw_to_bar(ntb, idx)];
3051291280Scem	if (bar->map_mode == mode)
3052291031Scem		return (0);
3053291031Scem
3054291280Scem	rc = pmap_change_attr((vm_offset_t)bar->vbase, bar->size, mode);
3055291031Scem	if (rc == 0)
3056291280Scem		bar->map_mode = mode;
3057291031Scem
3058291031Scem	return (rc);
3059291031Scem}
3060291031Scem
3061302484Smavstatic void
3062302484Smavntb_peer_db_set(device_t dev, uint64_t bit)
3063250079Scarl{
3064302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
3065250079Scarl
3066302484Smav	if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP)) {
3067295618Scem		struct ntb_pci_bar_info *lapic;
3068295618Scem		unsigned i;
3069295618Scem
3070295618Scem		lapic = ntb->peer_lapic_bar;
3071295618Scem
3072295618Scem		for (i = 0; i < XEON_NONLINK_DB_MSIX_BITS; i++) {
3073302484Smav			if ((bit & ntb_db_vector_mask(dev, i)) != 0)
3074295618Scem				bus_space_write_4(lapic->pci_bus_tag,
3075295618Scem				    lapic->pci_bus_handle,
3076295618Scem				    ntb->peer_msix_data[i].nmd_ofs,
3077295618Scem				    ntb->peer_msix_data[i].nmd_data);
3078295618Scem		}
3079295618Scem		return;
3080295618Scem	}
3081295618Scem
3082302484Smav	if (HAS_FEATURE(ntb, NTB_SDOORBELL_LOCKUP)) {
3083290682Scem		ntb_mw_write(2, XEON_PDOORBELL_OFFSET, bit);
3084289347Scem		return;
3085289209Scem	}
3086289347Scem
3087289546Scem	db_iowrite(ntb, ntb->peer_reg->db_bell, bit);
3088250079Scarl}
3089250079Scarl
3090302484Smavstatic int
3091302484Smavntb_peer_db_addr(device_t dev, bus_addr_t *db_addr, vm_size_t *db_size)
3092289542Scem{
3093302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
3094289542Scem	struct ntb_pci_bar_info *bar;
3095289542Scem	uint64_t regoff;
3096289542Scem
3097302484Smav	KASSERT((db_addr != NULL && db_size != NULL), ("must be non-NULL"));
3098289542Scem
3099302484Smav	if (!HAS_FEATURE(ntb, NTB_SDOORBELL_LOCKUP)) {
3100289542Scem		bar = &ntb->bar_info[NTB_CONFIG_BAR];
3101289542Scem		regoff = ntb->peer_reg->db_bell;
3102289542Scem	} else {
3103289543Scem		KASSERT(ntb->b2b_mw_idx != B2B_MW_DISABLED,
3104289543Scem		    ("invalid b2b idx"));
3105289542Scem
3106289542Scem		bar = &ntb->bar_info[ntb_mw_to_bar(ntb, ntb->b2b_mw_idx)];
3107290682Scem		regoff = XEON_PDOORBELL_OFFSET;
3108289542Scem	}
3109289542Scem	KASSERT(bar->pci_bus_tag != X86_BUS_SPACE_IO, ("uh oh"));
3110289542Scem
3111289542Scem	/* HACK: Specific to current x86 bus implementation. */
3112302484Smav	*db_addr = ((uint64_t)bar->pci_bus_handle + regoff);
3113302484Smav	*db_size = ntb->reg->db_size;
3114302484Smav	return (0);
3115289542Scem}
3116289542Scem
3117302484Smavstatic uint64_t
3118302484Smavntb_db_valid_mask(device_t dev)
3119289597Scem{
3120302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
3121289597Scem
3122289597Scem	return (ntb->db_valid_mask);
3123289597Scem}
3124289597Scem
3125302484Smavstatic int
3126302484Smavntb_db_vector_count(device_t dev)
3127289598Scem{
3128302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
3129289598Scem
3130302484Smav	return (ntb->db_vec_count);
3131302484Smav}
3132302484Smav
3133302484Smavstatic uint64_t
3134302484Smavntb_db_vector_mask(device_t dev, uint32_t vector)
3135302484Smav{
3136302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
3137302484Smav
3138289598Scem	if (vector > ntb->db_vec_count)
3139289598Scem		return (0);
3140289598Scem	return (ntb->db_valid_mask & ntb_vec_mask(ntb, vector));
3141289598Scem}
3142289598Scem
3143302484Smavstatic bool
3144302484Smavntb_link_is_up(device_t dev, enum ntb_speed *speed, enum ntb_width *width)
3145250079Scarl{
3146302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
3147250079Scarl
3148289546Scem	if (speed != NULL)
3149289546Scem		*speed = ntb_link_sta_speed(ntb);
3150289546Scem	if (width != NULL)
3151289546Scem		*width = ntb_link_sta_width(ntb);
3152289546Scem	return (link_is_up(ntb));
3153250079Scarl}
3154250079Scarl
3155255272Scarlstatic void
3156255272Scarlsave_bar_parameters(struct ntb_pci_bar_info *bar)
3157250079Scarl{
3158255272Scarl
3159289209Scem	bar->pci_bus_tag = rman_get_bustag(bar->pci_resource);
3160289209Scem	bar->pci_bus_handle = rman_get_bushandle(bar->pci_resource);
3161289209Scem	bar->pbase = rman_get_start(bar->pci_resource);
3162289209Scem	bar->size = rman_get_size(bar->pci_resource);
3163289209Scem	bar->vbase = rman_get_virtual(bar->pci_resource);
3164250079Scarl}
3165255268Scarl
3166302484Smavstatic device_method_t ntb_intel_methods[] = {
3167302484Smav	/* Device interface */
3168302484Smav	DEVMETHOD(device_probe,     ntb_probe),
3169302484Smav	DEVMETHOD(device_attach,    ntb_attach),
3170302484Smav	DEVMETHOD(device_detach,    ntb_detach),
3171302484Smav	/* NTB interface */
3172302484Smav	DEVMETHOD(ntb_link_is_up,	ntb_link_is_up),
3173302484Smav	DEVMETHOD(ntb_link_enable,	ntb_link_enable),
3174302484Smav	DEVMETHOD(ntb_link_disable,	ntb_link_disable),
3175302484Smav	DEVMETHOD(ntb_link_enabled,	ntb_link_enabled),
3176302484Smav	DEVMETHOD(ntb_set_ctx,		ntb_set_ctx),
3177302484Smav	DEVMETHOD(ntb_get_ctx,		ntb_get_ctx),
3178302484Smav	DEVMETHOD(ntb_clear_ctx,	ntb_clear_ctx),
3179302484Smav	DEVMETHOD(ntb_mw_count,		ntb_mw_count),
3180302484Smav	DEVMETHOD(ntb_mw_get_range,	ntb_mw_get_range),
3181302484Smav	DEVMETHOD(ntb_mw_set_trans,	ntb_mw_set_trans),
3182302484Smav	DEVMETHOD(ntb_mw_clear_trans,	ntb_mw_clear_trans),
3183302484Smav	DEVMETHOD(ntb_mw_get_wc,	ntb_mw_get_wc),
3184302484Smav	DEVMETHOD(ntb_mw_set_wc,	ntb_mw_set_wc),
3185302484Smav	DEVMETHOD(ntb_spad_count,	ntb_spad_count),
3186302484Smav	DEVMETHOD(ntb_spad_clear,	ntb_spad_clear),
3187302484Smav	DEVMETHOD(ntb_spad_write,	ntb_spad_write),
3188302484Smav	DEVMETHOD(ntb_spad_read,	ntb_spad_read),
3189302484Smav	DEVMETHOD(ntb_peer_spad_write,	ntb_peer_spad_write),
3190302484Smav	DEVMETHOD(ntb_peer_spad_read,	ntb_peer_spad_read),
3191302484Smav	DEVMETHOD(ntb_db_valid_mask,	ntb_db_valid_mask),
3192302484Smav	DEVMETHOD(ntb_db_vector_count,	ntb_db_vector_count),
3193302484Smav	DEVMETHOD(ntb_db_vector_mask,	ntb_db_vector_mask),
3194302484Smav	DEVMETHOD(ntb_db_clear,		ntb_db_clear),
3195302484Smav	DEVMETHOD(ntb_db_clear_mask,	ntb_db_clear_mask),
3196302484Smav	DEVMETHOD(ntb_db_read,		ntb_db_read),
3197302484Smav	DEVMETHOD(ntb_db_set_mask,	ntb_db_set_mask),
3198302484Smav	DEVMETHOD(ntb_peer_db_addr,	ntb_peer_db_addr),
3199302484Smav	DEVMETHOD(ntb_peer_db_set,	ntb_peer_db_set),
3200302484Smav	DEVMETHOD_END
3201302484Smav};
3202255268Scarl
3203302484Smavstatic DEFINE_CLASS_0(ntb_hw, ntb_intel_driver, ntb_intel_methods,
3204302484Smav    sizeof(struct ntb_softc));
3205302484SmavDRIVER_MODULE(ntb_intel, pci, ntb_intel_driver, ntb_hw_devclass, NULL, NULL);
3206302484SmavMODULE_DEPEND(ntb_intel, ntb, 1, 1, 1);
3207302484SmavMODULE_VERSION(ntb_intel, 1);
3208