ntb_hw_intel.c revision 302510
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 302510 2016-07-10 01:09:16Z mav $");
41250079Scarl
42250079Scarl#include <sys/param.h>
43250079Scarl#include <sys/kernel.h>
44250079Scarl#include <sys/systm.h>
45250079Scarl#include <sys/bus.h>
46289774Scem#include <sys/endian.h>
47302493Smav#include <sys/interrupt.h>
48250079Scarl#include <sys/malloc.h>
49250079Scarl#include <sys/module.h>
50295618Scem#include <sys/mutex.h>
51295618Scem#include <sys/pciio.h>
52250079Scarl#include <sys/queue.h>
53250079Scarl#include <sys/rman.h>
54302491Smav#include <sys/rmlock.h>
55289774Scem#include <sys/sbuf.h>
56289207Scem#include <sys/sysctl.h>
57250079Scarl#include <vm/vm.h>
58250079Scarl#include <vm/pmap.h>
59250079Scarl#include <machine/bus.h>
60295618Scem#include <machine/intr_machdep.h>
61250079Scarl#include <machine/resource.h>
62250079Scarl#include <dev/pci/pcireg.h>
63250079Scarl#include <dev/pci/pcivar.h>
64250079Scarl
65250079Scarl#include "ntb_regs.h"
66302484Smav#include "../ntb.h"
67250079Scarl
68289648Scem#define MAX_MSIX_INTERRUPTS MAX(XEON_DB_COUNT, ATOM_DB_COUNT)
69250079Scarl
70289539Scem#define NTB_HB_TIMEOUT		1 /* second */
71289648Scem#define ATOM_LINK_RECOVERY_TIME	500 /* ms */
72291032Scem#define BAR_HIGH_MASK		(~((1ull << 12) - 1))
73250079Scarl
74295618Scem#define	NTB_MSIX_VER_GUARD	0xaabbccdd
75295618Scem#define	NTB_MSIX_RECEIVED	0xe0f0e0f0
76295618Scem
77295618Scem/*
78295618Scem * PCI constants could be somewhere more generic, but aren't defined/used in
79295618Scem * pci.c.
80295618Scem */
81295618Scem#define	PCI_MSIX_ENTRY_SIZE		16
82295618Scem#define	PCI_MSIX_ENTRY_LOWER_ADDR	0
83295618Scem#define	PCI_MSIX_ENTRY_UPPER_ADDR	4
84295618Scem#define	PCI_MSIX_ENTRY_DATA		8
85295618Scem
86250079Scarlenum ntb_device_type {
87250079Scarl	NTB_XEON,
88289648Scem	NTB_ATOM
89250079Scarl};
90250079Scarl
91289610Scem/* ntb_conn_type are hardware numbers, cannot change. */
92289610Scemenum ntb_conn_type {
93289610Scem	NTB_CONN_TRANSPARENT = 0,
94289610Scem	NTB_CONN_B2B = 1,
95289610Scem	NTB_CONN_RP = 2,
96289610Scem};
97289610Scem
98289610Scemenum ntb_b2b_direction {
99289610Scem	NTB_DEV_USD = 0,
100289610Scem	NTB_DEV_DSD = 1,
101289610Scem};
102289610Scem
103289539Scemenum ntb_bar {
104289539Scem	NTB_CONFIG_BAR = 0,
105289539Scem	NTB_B2B_BAR_1,
106289539Scem	NTB_B2B_BAR_2,
107289539Scem	NTB_B2B_BAR_3,
108289539Scem	NTB_MAX_BARS
109289539Scem};
110289539Scem
111295618Scemenum {
112295618Scem	NTB_MSIX_GUARD = 0,
113295618Scem	NTB_MSIX_DATA0,
114295618Scem	NTB_MSIX_DATA1,
115295618Scem	NTB_MSIX_DATA2,
116295618Scem	NTB_MSIX_OFS0,
117295618Scem	NTB_MSIX_OFS1,
118295618Scem	NTB_MSIX_OFS2,
119295618Scem	NTB_MSIX_DONE,
120295618Scem	NTB_MAX_MSIX_SPAD
121295618Scem};
122295618Scem
123255274Scarl/* Device features and workarounds */
124302484Smav#define HAS_FEATURE(ntb, feature)	\
125302484Smav	(((ntb)->features & (feature)) != 0)
126255274Scarl
127250079Scarlstruct ntb_hw_info {
128250079Scarl	uint32_t		device_id;
129255274Scarl	const char		*desc;
130250079Scarl	enum ntb_device_type	type;
131289397Scem	uint32_t		features;
132250079Scarl};
133250079Scarl
134250079Scarlstruct ntb_pci_bar_info {
135250079Scarl	bus_space_tag_t		pci_bus_tag;
136250079Scarl	bus_space_handle_t	pci_bus_handle;
137250079Scarl	int			pci_resource_id;
138250079Scarl	struct resource		*pci_resource;
139250079Scarl	vm_paddr_t		pbase;
140290679Scem	caddr_t			vbase;
141290679Scem	vm_size_t		size;
142291280Scem	vm_memattr_t		map_mode;
143289543Scem
144289543Scem	/* Configuration register offsets */
145289543Scem	uint32_t		psz_off;
146289543Scem	uint32_t		ssz_off;
147289543Scem	uint32_t		pbarxlat_off;
148250079Scarl};
149250079Scarl
150250079Scarlstruct ntb_int_info {
151250079Scarl	struct resource	*res;
152250079Scarl	int		rid;
153250079Scarl	void		*tag;
154250079Scarl};
155250079Scarl
156289546Scemstruct ntb_vec {
157250079Scarl	struct ntb_softc	*ntb;
158289546Scem	uint32_t		num;
159295618Scem	unsigned		masked;
160250079Scarl};
161250079Scarl
162289542Scemstruct ntb_reg {
163289542Scem	uint32_t	ntb_ctl;
164289542Scem	uint32_t	lnk_sta;
165289542Scem	uint8_t		db_size;
166289542Scem	unsigned	mw_bar[NTB_MAX_BARS];
167289542Scem};
168289542Scem
169289542Scemstruct ntb_alt_reg {
170289542Scem	uint32_t	db_bell;
171289542Scem	uint32_t	db_mask;
172289542Scem	uint32_t	spad;
173289542Scem};
174289542Scem
175289542Scemstruct ntb_xlat_reg {
176289546Scem	uint32_t	bar0_base;
177289546Scem	uint32_t	bar2_base;
178289546Scem	uint32_t	bar4_base;
179289546Scem	uint32_t	bar5_base;
180289546Scem
181289546Scem	uint32_t	bar2_xlat;
182289546Scem	uint32_t	bar4_xlat;
183289546Scem	uint32_t	bar5_xlat;
184289546Scem
185289546Scem	uint32_t	bar2_limit;
186289546Scem	uint32_t	bar4_limit;
187289546Scem	uint32_t	bar5_limit;
188289542Scem};
189289542Scem
190289542Scemstruct ntb_b2b_addr {
191289542Scem	uint64_t	bar0_addr;
192289542Scem	uint64_t	bar2_addr64;
193289542Scem	uint64_t	bar4_addr64;
194289542Scem	uint64_t	bar4_addr32;
195289542Scem	uint64_t	bar5_addr32;
196289542Scem};
197289542Scem
198295618Scemstruct ntb_msix_data {
199295618Scem	uint32_t	nmd_ofs;
200295618Scem	uint32_t	nmd_data;
201295618Scem};
202295618Scem
203250079Scarlstruct ntb_softc {
204250079Scarl	device_t		device;
205250079Scarl	enum ntb_device_type	type;
206289774Scem	uint32_t		features;
207250079Scarl
208250079Scarl	struct ntb_pci_bar_info	bar_info[NTB_MAX_BARS];
209250079Scarl	struct ntb_int_info	int_info[MAX_MSIX_INTERRUPTS];
210250079Scarl	uint32_t		allocated_interrupts;
211250079Scarl
212295618Scem	struct ntb_msix_data	peer_msix_data[XEON_NONLINK_DB_MSIX_BITS];
213295618Scem	struct ntb_msix_data	msix_data[XEON_NONLINK_DB_MSIX_BITS];
214295618Scem	bool			peer_msix_good;
215295618Scem	bool			peer_msix_done;
216295618Scem	struct ntb_pci_bar_info	*peer_lapic_bar;
217295618Scem	struct callout		peer_msix_work;
218295618Scem
219250079Scarl	struct callout		heartbeat_timer;
220250079Scarl	struct callout		lr_timer;
221250079Scarl
222289546Scem	void			*ntb_ctx;
223289546Scem	const struct ntb_ctx_ops *ctx_ops;
224289546Scem	struct ntb_vec		*msix_vec;
225302491Smav	struct rmlock		ctx_lock;
226250079Scarl
227289610Scem	uint32_t		ppd;
228289610Scem	enum ntb_conn_type	conn_type;
229289610Scem	enum ntb_b2b_direction	dev_type;
230289539Scem
231289542Scem	/* Offset of peer bar0 in B2B BAR */
232289542Scem	uint64_t			b2b_off;
233289542Scem	/* Memory window used to access peer bar0 */
234289543Scem#define B2B_MW_DISABLED			UINT8_MAX
235289542Scem	uint8_t				b2b_mw_idx;
236301293Smav	uint32_t			msix_xlat;
237295618Scem	uint8_t				msix_mw_idx;
238289542Scem
239289539Scem	uint8_t				mw_count;
240289539Scem	uint8_t				spad_count;
241289539Scem	uint8_t				db_count;
242289539Scem	uint8_t				db_vec_count;
243289539Scem	uint8_t				db_vec_shift;
244289542Scem
245289546Scem	/* Protects local db_mask. */
246289546Scem#define DB_MASK_LOCK(sc)	mtx_lock_spin(&(sc)->db_mask_lock)
247289546Scem#define DB_MASK_UNLOCK(sc)	mtx_unlock_spin(&(sc)->db_mask_lock)
248289546Scem#define DB_MASK_ASSERT(sc,f)	mtx_assert(&(sc)->db_mask_lock, (f))
249289542Scem	struct mtx			db_mask_lock;
250289542Scem
251290686Scem	volatile uint32_t		ntb_ctl;
252290686Scem	volatile uint32_t		lnk_sta;
253289542Scem
254289542Scem	uint64_t			db_valid_mask;
255289542Scem	uint64_t			db_link_mask;
256289546Scem	uint64_t			db_mask;
257302493Smav	uint64_t			fake_db_bell;	/* NTB_SB01BASE_LOCKUP*/
258289542Scem
259289542Scem	int				last_ts;	/* ticks @ last irq */
260289542Scem
261289542Scem	const struct ntb_reg		*reg;
262289542Scem	const struct ntb_alt_reg	*self_reg;
263289542Scem	const struct ntb_alt_reg	*peer_reg;
264289542Scem	const struct ntb_xlat_reg	*xlat_reg;
265250079Scarl};
266250079Scarl
267289234Scem#ifdef __i386__
268289234Scemstatic __inline uint64_t
269289234Scembus_space_read_8(bus_space_tag_t tag, bus_space_handle_t handle,
270289234Scem    bus_size_t offset)
271289234Scem{
272289234Scem
273289234Scem	return (bus_space_read_4(tag, handle, offset) |
274289234Scem	    ((uint64_t)bus_space_read_4(tag, handle, offset + 4)) << 32);
275289234Scem}
276289234Scem
277289234Scemstatic __inline void
278289234Scembus_space_write_8(bus_space_tag_t tag, bus_space_handle_t handle,
279289234Scem    bus_size_t offset, uint64_t val)
280289234Scem{
281289234Scem
282289234Scem	bus_space_write_4(tag, handle, offset, val);
283289234Scem	bus_space_write_4(tag, handle, offset + 4, val >> 32);
284289234Scem}
285289234Scem#endif
286289234Scem
287255279Scarl#define ntb_bar_read(SIZE, bar, offset) \
288255279Scarl	    bus_space_read_ ## SIZE (ntb->bar_info[(bar)].pci_bus_tag, \
289255279Scarl	    ntb->bar_info[(bar)].pci_bus_handle, (offset))
290255279Scarl#define ntb_bar_write(SIZE, bar, offset, val) \
291255279Scarl	    bus_space_write_ ## SIZE (ntb->bar_info[(bar)].pci_bus_tag, \
292255279Scarl	    ntb->bar_info[(bar)].pci_bus_handle, (offset), (val))
293255279Scarl#define ntb_reg_read(SIZE, offset) ntb_bar_read(SIZE, NTB_CONFIG_BAR, offset)
294250079Scarl#define ntb_reg_write(SIZE, offset, val) \
295255279Scarl	    ntb_bar_write(SIZE, NTB_CONFIG_BAR, offset, val)
296289397Scem#define ntb_mw_read(SIZE, offset) \
297289542Scem	    ntb_bar_read(SIZE, ntb_mw_to_bar(ntb, ntb->b2b_mw_idx), offset)
298255279Scarl#define ntb_mw_write(SIZE, offset, val) \
299289542Scem	    ntb_bar_write(SIZE, ntb_mw_to_bar(ntb, ntb->b2b_mw_idx), \
300289397Scem		offset, val)
301250079Scarl
302250079Scarlstatic int ntb_probe(device_t device);
303250079Scarlstatic int ntb_attach(device_t device);
304250079Scarlstatic int ntb_detach(device_t device);
305302484Smavstatic uint64_t ntb_db_valid_mask(device_t dev);
306302484Smavstatic void ntb_spad_clear(device_t dev);
307302484Smavstatic uint64_t ntb_db_vector_mask(device_t dev, uint32_t vector);
308302484Smavstatic bool ntb_link_is_up(device_t dev, enum ntb_speed *speed,
309302484Smav    enum ntb_width *width);
310302484Smavstatic int ntb_link_enable(device_t dev, enum ntb_speed speed,
311302484Smav    enum ntb_width width);
312302484Smavstatic int ntb_link_disable(device_t dev);
313302484Smavstatic int ntb_spad_read(device_t dev, unsigned int idx, uint32_t *val);
314302484Smavstatic int ntb_peer_spad_write(device_t dev, unsigned int idx, uint32_t val);
315302484Smav
316291263Scemstatic unsigned ntb_user_mw_to_idx(struct ntb_softc *, unsigned uidx);
317289539Scemstatic inline enum ntb_bar ntb_mw_to_bar(struct ntb_softc *, unsigned mw);
318289546Scemstatic inline bool bar_is_64bit(struct ntb_softc *, enum ntb_bar);
319289546Scemstatic inline void bar_get_xlat_params(struct ntb_softc *, enum ntb_bar,
320289546Scem    uint32_t *base, uint32_t *xlat, uint32_t *lmt);
321255272Scarlstatic int ntb_map_pci_bars(struct ntb_softc *ntb);
322291280Scemstatic int ntb_mw_set_wc_internal(struct ntb_softc *, unsigned idx,
323291280Scem    vm_memattr_t);
324289647Scemstatic void print_map_success(struct ntb_softc *, struct ntb_pci_bar_info *,
325289647Scem    const char *);
326255272Scarlstatic int map_mmr_bar(struct ntb_softc *ntb, struct ntb_pci_bar_info *bar);
327255272Scarlstatic int map_memory_window_bar(struct ntb_softc *ntb,
328255272Scarl    struct ntb_pci_bar_info *bar);
329250079Scarlstatic void ntb_unmap_pci_bar(struct ntb_softc *ntb);
330289344Scemstatic int ntb_remap_msix(device_t, uint32_t desired, uint32_t avail);
331289540Scemstatic int ntb_init_isr(struct ntb_softc *ntb);
332289342Scemstatic int ntb_setup_legacy_interrupt(struct ntb_softc *ntb);
333289540Scemstatic int ntb_setup_msix(struct ntb_softc *ntb, uint32_t num_vectors);
334250079Scarlstatic void ntb_teardown_interrupts(struct ntb_softc *ntb);
335289540Scemstatic inline uint64_t ntb_vec_mask(struct ntb_softc *, uint64_t db_vector);
336289546Scemstatic void ntb_interrupt(struct ntb_softc *, uint32_t vec);
337289546Scemstatic void ndev_vec_isr(void *arg);
338289546Scemstatic void ndev_irq_isr(void *arg);
339289546Scemstatic inline uint64_t db_ioread(struct ntb_softc *, uint64_t regoff);
340290678Scemstatic inline void db_iowrite(struct ntb_softc *, uint64_t regoff, uint64_t);
341290678Scemstatic inline void db_iowrite_raw(struct ntb_softc *, uint64_t regoff, uint64_t);
342289546Scemstatic int ntb_create_msix_vec(struct ntb_softc *ntb, uint32_t num_vectors);
343289546Scemstatic void ntb_free_msix_vec(struct ntb_softc *ntb);
344300531Scemstatic void ntb_get_msix_info(struct ntb_softc *ntb);
345295618Scemstatic void ntb_exchange_msix(void *);
346250079Scarlstatic struct ntb_hw_info *ntb_get_device_info(uint32_t device_id);
347289397Scemstatic void ntb_detect_max_mw(struct ntb_softc *ntb);
348289348Scemstatic int ntb_detect_xeon(struct ntb_softc *ntb);
349289648Scemstatic int ntb_detect_atom(struct ntb_softc *ntb);
350289542Scemstatic int ntb_xeon_init_dev(struct ntb_softc *ntb);
351289648Scemstatic int ntb_atom_init_dev(struct ntb_softc *ntb);
352289272Scemstatic void ntb_teardown_xeon(struct ntb_softc *ntb);
353289648Scemstatic void configure_atom_secondary_side_bars(struct ntb_softc *ntb);
354289543Scemstatic void xeon_reset_sbar_size(struct ntb_softc *, enum ntb_bar idx,
355289543Scem    enum ntb_bar regbar);
356289543Scemstatic void xeon_set_sbar_base_and_limit(struct ntb_softc *,
357289543Scem    uint64_t base_addr, enum ntb_bar idx, enum ntb_bar regbar);
358289543Scemstatic void xeon_set_pbar_xlat(struct ntb_softc *, uint64_t base_addr,
359289543Scem    enum ntb_bar idx);
360289542Scemstatic int xeon_setup_b2b_mw(struct ntb_softc *,
361289542Scem    const struct ntb_b2b_addr *addr, const struct ntb_b2b_addr *peer_addr);
362289546Scemstatic inline bool link_is_up(struct ntb_softc *ntb);
363295618Scemstatic inline bool _xeon_link_is_up(struct ntb_softc *ntb);
364289648Scemstatic inline bool atom_link_is_err(struct ntb_softc *ntb);
365289546Scemstatic inline enum ntb_speed ntb_link_sta_speed(struct ntb_softc *);
366289546Scemstatic inline enum ntb_width ntb_link_sta_width(struct ntb_softc *);
367289648Scemstatic void atom_link_hb(void *arg);
368302484Smavstatic void ntb_link_event(device_t dev);
369302484Smavstatic void ntb_db_event(device_t dev, uint32_t vec);
370289648Scemstatic void recover_atom_link(void *arg);
371289546Scemstatic bool ntb_poll_link(struct ntb_softc *ntb);
372255274Scarlstatic void save_bar_parameters(struct ntb_pci_bar_info *bar);
373289774Scemstatic void ntb_sysctl_init(struct ntb_softc *);
374289774Scemstatic int sysctl_handle_features(SYSCTL_HANDLER_ARGS);
375300100Scemstatic int sysctl_handle_link_admin(SYSCTL_HANDLER_ARGS);
376300100Scemstatic int sysctl_handle_link_status_human(SYSCTL_HANDLER_ARGS);
377289774Scemstatic int sysctl_handle_link_status(SYSCTL_HANDLER_ARGS);
378289774Scemstatic int sysctl_handle_register(SYSCTL_HANDLER_ARGS);
379250079Scarl
380290685Scemstatic unsigned g_ntb_hw_debug_level;
381290685ScemSYSCTL_UINT(_hw_ntb, OID_AUTO, debug_level, CTLFLAG_RWTUN,
382290685Scem    &g_ntb_hw_debug_level, 0, "ntb_hw log level -- higher is more verbose");
383290685Scem#define ntb_printf(lvl, ...) do {				\
384290685Scem	if ((lvl) <= g_ntb_hw_debug_level) {			\
385290685Scem		device_printf(ntb->device, __VA_ARGS__);	\
386290685Scem	}							\
387290685Scem} while (0)
388290685Scem
389295486Scem#define	_NTB_PAT_UC	0
390295486Scem#define	_NTB_PAT_WC	1
391295486Scem#define	_NTB_PAT_WT	4
392295486Scem#define	_NTB_PAT_WP	5
393295486Scem#define	_NTB_PAT_WB	6
394295486Scem#define	_NTB_PAT_UCM	7
395295486Scemstatic unsigned g_ntb_mw_pat = _NTB_PAT_UC;
396295486ScemSYSCTL_UINT(_hw_ntb, OID_AUTO, default_mw_pat, CTLFLAG_RDTUN,
397295486Scem    &g_ntb_mw_pat, 0, "Configure the default memory window cache flags (PAT): "
398295486Scem    "UC: "  __XSTRING(_NTB_PAT_UC) ", "
399295486Scem    "WC: "  __XSTRING(_NTB_PAT_WC) ", "
400295486Scem    "WT: "  __XSTRING(_NTB_PAT_WT) ", "
401295486Scem    "WP: "  __XSTRING(_NTB_PAT_WP) ", "
402295486Scem    "WB: "  __XSTRING(_NTB_PAT_WB) ", "
403295486Scem    "UC-: " __XSTRING(_NTB_PAT_UCM));
404291030Scem
405295486Scemstatic inline vm_memattr_t
406295486Scemntb_pat_flags(void)
407295486Scem{
408295486Scem
409295486Scem	switch (g_ntb_mw_pat) {
410295486Scem	case _NTB_PAT_WC:
411295486Scem		return (VM_MEMATTR_WRITE_COMBINING);
412295486Scem	case _NTB_PAT_WT:
413295486Scem		return (VM_MEMATTR_WRITE_THROUGH);
414295486Scem	case _NTB_PAT_WP:
415295486Scem		return (VM_MEMATTR_WRITE_PROTECTED);
416295486Scem	case _NTB_PAT_WB:
417295486Scem		return (VM_MEMATTR_WRITE_BACK);
418295486Scem	case _NTB_PAT_UCM:
419295486Scem		return (VM_MEMATTR_WEAK_UNCACHEABLE);
420295486Scem	case _NTB_PAT_UC:
421295486Scem		/* FALLTHROUGH */
422295486Scem	default:
423295486Scem		return (VM_MEMATTR_UNCACHEABLE);
424295486Scem	}
425295486Scem}
426295486Scem
427295487Scem/*
428295487Scem * Well, this obviously doesn't belong here, but it doesn't seem to exist
429295487Scem * anywhere better yet.
430295487Scem */
431295487Scemstatic inline const char *
432295487Scemntb_vm_memattr_to_str(vm_memattr_t pat)
433295487Scem{
434295487Scem
435295487Scem	switch (pat) {
436295487Scem	case VM_MEMATTR_WRITE_COMBINING:
437295487Scem		return ("WRITE_COMBINING");
438295487Scem	case VM_MEMATTR_WRITE_THROUGH:
439295487Scem		return ("WRITE_THROUGH");
440295487Scem	case VM_MEMATTR_WRITE_PROTECTED:
441295487Scem		return ("WRITE_PROTECTED");
442295487Scem	case VM_MEMATTR_WRITE_BACK:
443295487Scem		return ("WRITE_BACK");
444295487Scem	case VM_MEMATTR_WEAK_UNCACHEABLE:
445295487Scem		return ("UNCACHED");
446295487Scem	case VM_MEMATTR_UNCACHEABLE:
447295487Scem		return ("UNCACHEABLE");
448295487Scem	default:
449295487Scem		return ("UNKNOWN");
450295487Scem	}
451295487Scem}
452295487Scem
453302508Smavstatic int g_ntb_msix_idx = 1;
454295618ScemSYSCTL_INT(_hw_ntb, OID_AUTO, msix_mw_idx, CTLFLAG_RDTUN, &g_ntb_msix_idx,
455295618Scem    0, "Use this memory window to access the peer MSIX message complex on "
456295618Scem    "certain Xeon-based NTB systems, as a workaround for a hardware errata.  "
457295618Scem    "Like b2b_mw_idx, negative values index from the last available memory "
458295618Scem    "window.  (Applies on Xeon platforms with SB01BASE_LOCKUP errata.)");
459295618Scem
460291263Scemstatic int g_ntb_mw_idx = -1;
461291263ScemSYSCTL_INT(_hw_ntb, OID_AUTO, b2b_mw_idx, CTLFLAG_RDTUN, &g_ntb_mw_idx,
462291263Scem    0, "Use this memory window to access the peer NTB registers.  A "
463291263Scem    "non-negative value starts from the first MW index; a negative value "
464291263Scem    "starts from the last MW index.  The default is -1, i.e., the last "
465291263Scem    "available memory window.  Both sides of the NTB MUST set the same "
466291263Scem    "value here!  (Applies on Xeon platforms with SDOORBELL_LOCKUP errata.)");
467291263Scem
468302484Smav/* Hardware owns the low 16 bits of features. */
469302484Smav#define NTB_BAR_SIZE_4K		(1 << 0)
470302484Smav#define NTB_SDOORBELL_LOCKUP	(1 << 1)
471302484Smav#define NTB_SB01BASE_LOCKUP	(1 << 2)
472302484Smav#define NTB_B2BDOORBELL_BIT14	(1 << 3)
473302484Smav/* Software/configuration owns the top 16 bits. */
474302484Smav#define NTB_SPLIT_BAR		(1ull << 16)
475302484Smav
476302484Smav#define NTB_FEATURES_STR \
477302484Smav    "\20\21SPLIT_BAR4\04B2B_DOORBELL_BIT14\03SB01BASE_LOCKUP" \
478302484Smav    "\02SDOORBELL_LOCKUP\01BAR_SIZE_4K"
479302484Smav
480250079Scarlstatic struct ntb_hw_info pci_ids[] = {
481289612Scem	/* XXX: PS/SS IDs left out until they are supported. */
482289612Scem	{ 0x0C4E8086, "BWD Atom Processor S1200 Non-Transparent Bridge B2B",
483289648Scem		NTB_ATOM, 0 },
484289233Scem
485289233Scem	{ 0x37258086, "JSF Xeon C35xx/C55xx Non-Transparent Bridge B2B",
486289538Scem		NTB_XEON, NTB_SDOORBELL_LOCKUP | NTB_B2BDOORBELL_BIT14 },
487289233Scem	{ 0x3C0D8086, "SNB Xeon E5/Core i7 Non-Transparent Bridge B2B",
488289538Scem		NTB_XEON, NTB_SDOORBELL_LOCKUP | NTB_B2BDOORBELL_BIT14 },
489289233Scem	{ 0x0E0D8086, "IVT Xeon E5 V2 Non-Transparent Bridge B2B", NTB_XEON,
490289538Scem		NTB_SDOORBELL_LOCKUP | NTB_B2BDOORBELL_BIT14 |
491289538Scem		    NTB_SB01BASE_LOCKUP | NTB_BAR_SIZE_4K },
492289233Scem	{ 0x2F0D8086, "HSX Xeon E5 V3 Non-Transparent Bridge B2B", NTB_XEON,
493289538Scem		NTB_SDOORBELL_LOCKUP | NTB_B2BDOORBELL_BIT14 |
494289538Scem		    NTB_SB01BASE_LOCKUP },
495289233Scem	{ 0x6F0D8086, "BDX Xeon E5 V4 Non-Transparent Bridge B2B", NTB_XEON,
496289538Scem		NTB_SDOORBELL_LOCKUP | NTB_B2BDOORBELL_BIT14 |
497289538Scem		    NTB_SB01BASE_LOCKUP },
498289233Scem
499289648Scem	{ 0x00000000, NULL, NTB_ATOM, 0 }
500250079Scarl};
501250079Scarl
502289648Scemstatic const struct ntb_reg atom_reg = {
503289648Scem	.ntb_ctl = ATOM_NTBCNTL_OFFSET,
504289648Scem	.lnk_sta = ATOM_LINK_STATUS_OFFSET,
505289542Scem	.db_size = sizeof(uint64_t),
506289542Scem	.mw_bar = { NTB_B2B_BAR_1, NTB_B2B_BAR_2 },
507289542Scem};
508289542Scem
509289648Scemstatic const struct ntb_alt_reg atom_pri_reg = {
510289648Scem	.db_bell = ATOM_PDOORBELL_OFFSET,
511289648Scem	.db_mask = ATOM_PDBMSK_OFFSET,
512289648Scem	.spad = ATOM_SPAD_OFFSET,
513289607Scem};
514289607Scem
515289648Scemstatic const struct ntb_alt_reg atom_b2b_reg = {
516289648Scem	.db_bell = ATOM_B2B_DOORBELL_OFFSET,
517289648Scem	.spad = ATOM_B2B_SPAD_OFFSET,
518289542Scem};
519289542Scem
520289648Scemstatic const struct ntb_xlat_reg atom_sec_xlat = {
521289542Scem#if 0
522289542Scem	/* "FIXME" says the Linux driver. */
523289648Scem	.bar0_base = ATOM_SBAR0BASE_OFFSET,
524289648Scem	.bar2_base = ATOM_SBAR2BASE_OFFSET,
525289648Scem	.bar4_base = ATOM_SBAR4BASE_OFFSET,
526289546Scem
527289648Scem	.bar2_limit = ATOM_SBAR2LMT_OFFSET,
528289648Scem	.bar4_limit = ATOM_SBAR4LMT_OFFSET,
529289542Scem#endif
530289546Scem
531289648Scem	.bar2_xlat = ATOM_SBAR2XLAT_OFFSET,
532289648Scem	.bar4_xlat = ATOM_SBAR4XLAT_OFFSET,
533289542Scem};
534289542Scem
535289542Scemstatic const struct ntb_reg xeon_reg = {
536289542Scem	.ntb_ctl = XEON_NTBCNTL_OFFSET,
537289542Scem	.lnk_sta = XEON_LINK_STATUS_OFFSET,
538289542Scem	.db_size = sizeof(uint16_t),
539289542Scem	.mw_bar = { NTB_B2B_BAR_1, NTB_B2B_BAR_2, NTB_B2B_BAR_3 },
540289542Scem};
541289542Scem
542289607Scemstatic const struct ntb_alt_reg xeon_pri_reg = {
543289607Scem	.db_bell = XEON_PDOORBELL_OFFSET,
544289607Scem	.db_mask = XEON_PDBMSK_OFFSET,
545289607Scem	.spad = XEON_SPAD_OFFSET,
546289607Scem};
547289607Scem
548289542Scemstatic const struct ntb_alt_reg xeon_b2b_reg = {
549289542Scem	.db_bell = XEON_B2B_DOORBELL_OFFSET,
550289542Scem	.spad = XEON_B2B_SPAD_OFFSET,
551289542Scem};
552289542Scem
553289542Scemstatic const struct ntb_xlat_reg xeon_sec_xlat = {
554289542Scem	.bar0_base = XEON_SBAR0BASE_OFFSET,
555289546Scem	.bar2_base = XEON_SBAR2BASE_OFFSET,
556289546Scem	.bar4_base = XEON_SBAR4BASE_OFFSET,
557289546Scem	.bar5_base = XEON_SBAR5BASE_OFFSET,
558289546Scem
559289542Scem	.bar2_limit = XEON_SBAR2LMT_OFFSET,
560289546Scem	.bar4_limit = XEON_SBAR4LMT_OFFSET,
561289546Scem	.bar5_limit = XEON_SBAR5LMT_OFFSET,
562289546Scem
563289542Scem	.bar2_xlat = XEON_SBAR2XLAT_OFFSET,
564289546Scem	.bar4_xlat = XEON_SBAR4XLAT_OFFSET,
565289546Scem	.bar5_xlat = XEON_SBAR5XLAT_OFFSET,
566289542Scem};
567289542Scem
568289614Scemstatic struct ntb_b2b_addr xeon_b2b_usd_addr = {
569290725Scem	.bar0_addr = XEON_B2B_BAR0_ADDR,
570290725Scem	.bar2_addr64 = XEON_B2B_BAR2_ADDR64,
571290725Scem	.bar4_addr64 = XEON_B2B_BAR4_ADDR64,
572290725Scem	.bar4_addr32 = XEON_B2B_BAR4_ADDR32,
573290725Scem	.bar5_addr32 = XEON_B2B_BAR5_ADDR32,
574289542Scem};
575289542Scem
576289614Scemstatic struct ntb_b2b_addr xeon_b2b_dsd_addr = {
577290725Scem	.bar0_addr = XEON_B2B_BAR0_ADDR,
578290725Scem	.bar2_addr64 = XEON_B2B_BAR2_ADDR64,
579290725Scem	.bar4_addr64 = XEON_B2B_BAR4_ADDR64,
580290725Scem	.bar4_addr32 = XEON_B2B_BAR4_ADDR32,
581290725Scem	.bar5_addr32 = XEON_B2B_BAR5_ADDR32,
582289542Scem};
583289542Scem
584289614ScemSYSCTL_NODE(_hw_ntb, OID_AUTO, xeon_b2b, CTLFLAG_RW, 0,
585289614Scem    "B2B MW segment overrides -- MUST be the same on both sides");
586289614Scem
587289614ScemSYSCTL_UQUAD(_hw_ntb_xeon_b2b, OID_AUTO, usd_bar2_addr64, CTLFLAG_RDTUN,
588289614Scem    &xeon_b2b_usd_addr.bar2_addr64, 0, "If using B2B topology on Xeon "
589289614Scem    "hardware, use this 64-bit address on the bus between the NTB devices for "
590289614Scem    "the window at BAR2, on the upstream side of the link.  MUST be the same "
591289614Scem    "address on both sides.");
592289614ScemSYSCTL_UQUAD(_hw_ntb_xeon_b2b, OID_AUTO, usd_bar4_addr64, CTLFLAG_RDTUN,
593289614Scem    &xeon_b2b_usd_addr.bar4_addr64, 0, "See usd_bar2_addr64, but BAR4.");
594289614ScemSYSCTL_UQUAD(_hw_ntb_xeon_b2b, OID_AUTO, usd_bar4_addr32, CTLFLAG_RDTUN,
595289614Scem    &xeon_b2b_usd_addr.bar4_addr32, 0, "See usd_bar2_addr64, but BAR4 "
596289614Scem    "(split-BAR mode).");
597289614ScemSYSCTL_UQUAD(_hw_ntb_xeon_b2b, OID_AUTO, usd_bar5_addr32, CTLFLAG_RDTUN,
598289646Scem    &xeon_b2b_usd_addr.bar5_addr32, 0, "See usd_bar2_addr64, but BAR5 "
599289614Scem    "(split-BAR mode).");
600289614Scem
601289614ScemSYSCTL_UQUAD(_hw_ntb_xeon_b2b, OID_AUTO, dsd_bar2_addr64, CTLFLAG_RDTUN,
602289614Scem    &xeon_b2b_dsd_addr.bar2_addr64, 0, "If using B2B topology on Xeon "
603289614Scem    "hardware, use this 64-bit address on the bus between the NTB devices for "
604289614Scem    "the window at BAR2, on the downstream side of the link.  MUST be the same"
605289614Scem    " address on both sides.");
606289614ScemSYSCTL_UQUAD(_hw_ntb_xeon_b2b, OID_AUTO, dsd_bar4_addr64, CTLFLAG_RDTUN,
607289614Scem    &xeon_b2b_dsd_addr.bar4_addr64, 0, "See dsd_bar2_addr64, but BAR4.");
608289614ScemSYSCTL_UQUAD(_hw_ntb_xeon_b2b, OID_AUTO, dsd_bar4_addr32, CTLFLAG_RDTUN,
609289614Scem    &xeon_b2b_dsd_addr.bar4_addr32, 0, "See dsd_bar2_addr64, but BAR4 "
610289614Scem    "(split-BAR mode).");
611289614ScemSYSCTL_UQUAD(_hw_ntb_xeon_b2b, OID_AUTO, dsd_bar5_addr32, CTLFLAG_RDTUN,
612289646Scem    &xeon_b2b_dsd_addr.bar5_addr32, 0, "See dsd_bar2_addr64, but BAR5 "
613289614Scem    "(split-BAR mode).");
614289614Scem
615250079Scarl/*
616250079Scarl * OS <-> Driver interface structures
617250079Scarl */
618250079ScarlMALLOC_DEFINE(M_NTB, "ntb_hw", "ntb_hw driver memory allocations");
619250079Scarl
620289207ScemSYSCTL_NODE(_hw, OID_AUTO, ntb, CTLFLAG_RW, 0, "NTB sysctls");
621289207Scem
622250079Scarl/*
623250079Scarl * OS <-> Driver linkage functions
624250079Scarl */
625250079Scarlstatic int
626250079Scarlntb_probe(device_t device)
627250079Scarl{
628289209Scem	struct ntb_hw_info *p;
629250079Scarl
630289209Scem	p = ntb_get_device_info(pci_get_devid(device));
631289209Scem	if (p == NULL)
632250079Scarl		return (ENXIO);
633289209Scem
634289209Scem	device_set_desc(device, p->desc);
635289209Scem	return (0);
636250079Scarl}
637250079Scarl
638250079Scarlstatic int
639250079Scarlntb_attach(device_t device)
640250079Scarl{
641289209Scem	struct ntb_softc *ntb;
642289209Scem	struct ntb_hw_info *p;
643250079Scarl	int error;
644250079Scarl
645302484Smav	ntb = device_get_softc(device);
646289209Scem	p = ntb_get_device_info(pci_get_devid(device));
647289209Scem
648250079Scarl	ntb->device = device;
649250079Scarl	ntb->type = p->type;
650255274Scarl	ntb->features = p->features;
651289543Scem	ntb->b2b_mw_idx = B2B_MW_DISABLED;
652295618Scem	ntb->msix_mw_idx = B2B_MW_DISABLED;
653250079Scarl
654289648Scem	/* Heartbeat timer for NTB_ATOM since there is no link interrupt */
655283291Sjkim	callout_init(&ntb->heartbeat_timer, 1);
656283291Sjkim	callout_init(&ntb->lr_timer, 1);
657295618Scem	callout_init(&ntb->peer_msix_work, 1);
658289542Scem	mtx_init(&ntb->db_mask_lock, "ntb hw bits", NULL, MTX_SPIN);
659302491Smav	rm_init(&ntb->ctx_lock, "ntb ctx");
660250079Scarl
661289648Scem	if (ntb->type == NTB_ATOM)
662289648Scem		error = ntb_detect_atom(ntb);
663289348Scem	else
664289348Scem		error = ntb_detect_xeon(ntb);
665290682Scem	if (error != 0)
666289348Scem		goto out;
667289348Scem
668289397Scem	ntb_detect_max_mw(ntb);
669289396Scem
670290682Scem	pci_enable_busmaster(ntb->device);
671290682Scem
672289209Scem	error = ntb_map_pci_bars(ntb);
673290682Scem	if (error != 0)
674289209Scem		goto out;
675289648Scem	if (ntb->type == NTB_ATOM)
676289648Scem		error = ntb_atom_init_dev(ntb);
677289272Scem	else
678289542Scem		error = ntb_xeon_init_dev(ntb);
679290682Scem	if (error != 0)
680289209Scem		goto out;
681290682Scem
682302484Smav	ntb_spad_clear(device);
683295618Scem
684290682Scem	ntb_poll_link(ntb);
685290682Scem
686289774Scem	ntb_sysctl_init(ntb);
687250079Scarl
688302484Smav	/* Attach children to this controller */
689302484Smav	device_add_child(device, NULL, -1);
690302484Smav	bus_generic_attach(device);
691302484Smav
692289209Scemout:
693289209Scem	if (error != 0)
694289209Scem		ntb_detach(device);
695250079Scarl	return (error);
696250079Scarl}
697250079Scarl
698250079Scarlstatic int
699250079Scarlntb_detach(device_t device)
700250079Scarl{
701289209Scem	struct ntb_softc *ntb;
702250079Scarl
703302484Smav	ntb = device_get_softc(device);
704289542Scem
705302484Smav	/* Detach & delete all children */
706302484Smav	device_delete_children(device);
707302484Smav
708295618Scem	if (ntb->self_reg != NULL) {
709295618Scem		DB_MASK_LOCK(ntb);
710295618Scem		db_iowrite(ntb, ntb->self_reg->db_mask, ntb->db_valid_mask);
711295618Scem		DB_MASK_UNLOCK(ntb);
712295618Scem	}
713250079Scarl	callout_drain(&ntb->heartbeat_timer);
714250079Scarl	callout_drain(&ntb->lr_timer);
715295618Scem	callout_drain(&ntb->peer_msix_work);
716290682Scem	pci_disable_busmaster(ntb->device);
717289272Scem	if (ntb->type == NTB_XEON)
718289272Scem		ntb_teardown_xeon(ntb);
719250079Scarl	ntb_teardown_interrupts(ntb);
720289397Scem
721289542Scem	mtx_destroy(&ntb->db_mask_lock);
722302491Smav	rm_destroy(&ntb->ctx_lock);
723289542Scem
724250079Scarl	ntb_unmap_pci_bar(ntb);
725250079Scarl
726250079Scarl	return (0);
727250079Scarl}
728250079Scarl
729289542Scem/*
730289542Scem * Driver internal routines
731289542Scem */
732289539Scemstatic inline enum ntb_bar
733289539Scemntb_mw_to_bar(struct ntb_softc *ntb, unsigned mw)
734289539Scem{
735289539Scem
736291263Scem	KASSERT(mw < ntb->mw_count,
737289542Scem	    ("%s: mw:%u > count:%u", __func__, mw, (unsigned)ntb->mw_count));
738289546Scem	KASSERT(ntb->reg->mw_bar[mw] != 0, ("invalid mw"));
739289539Scem
740289542Scem	return (ntb->reg->mw_bar[mw]);
741289539Scem}
742289539Scem
743289546Scemstatic inline bool
744289546Scembar_is_64bit(struct ntb_softc *ntb, enum ntb_bar bar)
745289546Scem{
746289546Scem	/* XXX This assertion could be stronger. */
747289546Scem	KASSERT(bar < NTB_MAX_BARS, ("bogus bar"));
748302484Smav	return (bar < NTB_B2B_BAR_2 || !HAS_FEATURE(ntb, NTB_SPLIT_BAR));
749289546Scem}
750289546Scem
751289546Scemstatic inline void
752289546Scembar_get_xlat_params(struct ntb_softc *ntb, enum ntb_bar bar, uint32_t *base,
753289546Scem    uint32_t *xlat, uint32_t *lmt)
754289546Scem{
755289546Scem	uint32_t basev, lmtv, xlatv;
756289546Scem
757289546Scem	switch (bar) {
758289546Scem	case NTB_B2B_BAR_1:
759289546Scem		basev = ntb->xlat_reg->bar2_base;
760289546Scem		lmtv = ntb->xlat_reg->bar2_limit;
761289546Scem		xlatv = ntb->xlat_reg->bar2_xlat;
762289546Scem		break;
763289546Scem	case NTB_B2B_BAR_2:
764289546Scem		basev = ntb->xlat_reg->bar4_base;
765289546Scem		lmtv = ntb->xlat_reg->bar4_limit;
766289546Scem		xlatv = ntb->xlat_reg->bar4_xlat;
767289546Scem		break;
768289546Scem	case NTB_B2B_BAR_3:
769289546Scem		basev = ntb->xlat_reg->bar5_base;
770289546Scem		lmtv = ntb->xlat_reg->bar5_limit;
771289546Scem		xlatv = ntb->xlat_reg->bar5_xlat;
772289546Scem		break;
773289546Scem	default:
774289546Scem		KASSERT(bar >= NTB_B2B_BAR_1 && bar < NTB_MAX_BARS,
775289546Scem		    ("bad bar"));
776289546Scem		basev = lmtv = xlatv = 0;
777289546Scem		break;
778289546Scem	}
779289546Scem
780289546Scem	if (base != NULL)
781289546Scem		*base = basev;
782289546Scem	if (xlat != NULL)
783289546Scem		*xlat = xlatv;
784289546Scem	if (lmt != NULL)
785289546Scem		*lmt = lmtv;
786289546Scem}
787289546Scem
788250079Scarlstatic int
789255272Scarlntb_map_pci_bars(struct ntb_softc *ntb)
790250079Scarl{
791255272Scarl	int rc;
792250079Scarl
793250079Scarl	ntb->bar_info[NTB_CONFIG_BAR].pci_resource_id = PCIR_BAR(0);
794289541Scem	rc = map_mmr_bar(ntb, &ntb->bar_info[NTB_CONFIG_BAR]);
795255272Scarl	if (rc != 0)
796289541Scem		goto out;
797255272Scarl
798289209Scem	ntb->bar_info[NTB_B2B_BAR_1].pci_resource_id = PCIR_BAR(2);
799289541Scem	rc = map_memory_window_bar(ntb, &ntb->bar_info[NTB_B2B_BAR_1]);
800255272Scarl	if (rc != 0)
801289541Scem		goto out;
802289543Scem	ntb->bar_info[NTB_B2B_BAR_1].psz_off = XEON_PBAR23SZ_OFFSET;
803289543Scem	ntb->bar_info[NTB_B2B_BAR_1].ssz_off = XEON_SBAR23SZ_OFFSET;
804289543Scem	ntb->bar_info[NTB_B2B_BAR_1].pbarxlat_off = XEON_PBAR2XLAT_OFFSET;
805255272Scarl
806289209Scem	ntb->bar_info[NTB_B2B_BAR_2].pci_resource_id = PCIR_BAR(4);
807291263Scem	rc = map_memory_window_bar(ntb, &ntb->bar_info[NTB_B2B_BAR_2]);
808291263Scem	if (rc != 0)
809291263Scem		goto out;
810289543Scem	ntb->bar_info[NTB_B2B_BAR_2].psz_off = XEON_PBAR4SZ_OFFSET;
811289543Scem	ntb->bar_info[NTB_B2B_BAR_2].ssz_off = XEON_SBAR4SZ_OFFSET;
812289543Scem	ntb->bar_info[NTB_B2B_BAR_2].pbarxlat_off = XEON_PBAR4XLAT_OFFSET;
813289543Scem
814302484Smav	if (!HAS_FEATURE(ntb, NTB_SPLIT_BAR))
815289541Scem		goto out;
816289397Scem
817289397Scem	ntb->bar_info[NTB_B2B_BAR_3].pci_resource_id = PCIR_BAR(5);
818291263Scem	rc = map_memory_window_bar(ntb, &ntb->bar_info[NTB_B2B_BAR_3]);
819289543Scem	ntb->bar_info[NTB_B2B_BAR_3].psz_off = XEON_PBAR5SZ_OFFSET;
820289543Scem	ntb->bar_info[NTB_B2B_BAR_3].ssz_off = XEON_SBAR5SZ_OFFSET;
821289543Scem	ntb->bar_info[NTB_B2B_BAR_3].pbarxlat_off = XEON_PBAR5XLAT_OFFSET;
822250079Scarl
823289541Scemout:
824289209Scem	if (rc != 0)
825255272Scarl		device_printf(ntb->device,
826255272Scarl		    "unable to allocate pci resource\n");
827255272Scarl	return (rc);
828255272Scarl}
829255272Scarl
830289541Scemstatic void
831289647Scemprint_map_success(struct ntb_softc *ntb, struct ntb_pci_bar_info *bar,
832289647Scem    const char *kind)
833289541Scem{
834289541Scem
835289647Scem	device_printf(ntb->device,
836289647Scem	    "Mapped BAR%d v:[%p-%p] p:[%p-%p] (0x%jx bytes) (%s)\n",
837289647Scem	    PCI_RID2BAR(bar->pci_resource_id), bar->vbase,
838289647Scem	    (char *)bar->vbase + bar->size - 1,
839289647Scem	    (void *)bar->pbase, (void *)(bar->pbase + bar->size - 1),
840289647Scem	    (uintmax_t)bar->size, kind);
841289541Scem}
842289541Scem
843255272Scarlstatic int
844255272Scarlmap_mmr_bar(struct ntb_softc *ntb, struct ntb_pci_bar_info *bar)
845255272Scarl{
846255272Scarl
847255275Scarl	bar->pci_resource = bus_alloc_resource_any(ntb->device, SYS_RES_MEMORY,
848289209Scem	    &bar->pci_resource_id, RF_ACTIVE);
849255272Scarl	if (bar->pci_resource == NULL)
850255272Scarl		return (ENXIO);
851289209Scem
852289209Scem	save_bar_parameters(bar);
853291280Scem	bar->map_mode = VM_MEMATTR_UNCACHEABLE;
854289647Scem	print_map_success(ntb, bar, "mmr");
855289209Scem	return (0);
856255272Scarl}
857255272Scarl
858255272Scarlstatic int
859255272Scarlmap_memory_window_bar(struct ntb_softc *ntb, struct ntb_pci_bar_info *bar)
860255272Scarl{
861255272Scarl	int rc;
862291280Scem	vm_memattr_t mapmode;
863255276Scarl	uint8_t bar_size_bits = 0;
864255272Scarl
865289209Scem	bar->pci_resource = bus_alloc_resource_any(ntb->device, SYS_RES_MEMORY,
866289209Scem	    &bar->pci_resource_id, RF_ACTIVE);
867250079Scarl
868255272Scarl	if (bar->pci_resource == NULL)
869255272Scarl		return (ENXIO);
870255276Scarl
871289209Scem	save_bar_parameters(bar);
872289209Scem	/*
873289209Scem	 * Ivytown NTB BAR sizes are misreported by the hardware due to a
874289209Scem	 * hardware issue. To work around this, query the size it should be
875289209Scem	 * configured to by the device and modify the resource to correspond to
876289209Scem	 * this new size. The BIOS on systems with this problem is required to
877289209Scem	 * provide enough address space to allow the driver to make this change
878289209Scem	 * safely.
879289209Scem	 *
880289209Scem	 * Ideally I could have just specified the size when I allocated the
881289209Scem	 * resource like:
882289209Scem	 *  bus_alloc_resource(ntb->device,
883289209Scem	 *	SYS_RES_MEMORY, &bar->pci_resource_id, 0ul, ~0ul,
884289209Scem	 *	1ul << bar_size_bits, RF_ACTIVE);
885289209Scem	 * but the PCI driver does not honor the size in this call, so we have
886289209Scem	 * to modify it after the fact.
887289209Scem	 */
888302484Smav	if (HAS_FEATURE(ntb, NTB_BAR_SIZE_4K)) {
889289209Scem		if (bar->pci_resource_id == PCIR_BAR(2))
890289209Scem			bar_size_bits = pci_read_config(ntb->device,
891289209Scem			    XEON_PBAR23SZ_OFFSET, 1);
892289209Scem		else
893289209Scem			bar_size_bits = pci_read_config(ntb->device,
894289209Scem			    XEON_PBAR45SZ_OFFSET, 1);
895289209Scem
896289209Scem		rc = bus_adjust_resource(ntb->device, SYS_RES_MEMORY,
897289209Scem		    bar->pci_resource, bar->pbase,
898289209Scem		    bar->pbase + (1ul << bar_size_bits) - 1);
899255272Scarl		if (rc != 0) {
900289209Scem			device_printf(ntb->device,
901289209Scem			    "unable to resize bar\n");
902255272Scarl			return (rc);
903250079Scarl		}
904289209Scem
905289209Scem		save_bar_parameters(bar);
906250079Scarl	}
907289209Scem
908291280Scem	bar->map_mode = VM_MEMATTR_UNCACHEABLE;
909291030Scem	print_map_success(ntb, bar, "mw");
910291280Scem
911295486Scem	/*
912295486Scem	 * Optionally, mark MW BARs as anything other than UC to improve
913295486Scem	 * performance.
914295486Scem	 */
915295486Scem	mapmode = ntb_pat_flags();
916295486Scem	if (mapmode == bar->map_mode)
917295486Scem		return (0);
918291030Scem
919291280Scem	rc = pmap_change_attr((vm_offset_t)bar->vbase, bar->size, mapmode);
920291031Scem	if (rc == 0) {
921291280Scem		bar->map_mode = mapmode;
922289209Scem		device_printf(ntb->device,
923289647Scem		    "Marked BAR%d v:[%p-%p] p:[%p-%p] as "
924291280Scem		    "%s.\n",
925289647Scem		    PCI_RID2BAR(bar->pci_resource_id), bar->vbase,
926289647Scem		    (char *)bar->vbase + bar->size - 1,
927291280Scem		    (void *)bar->pbase, (void *)(bar->pbase + bar->size - 1),
928295487Scem		    ntb_vm_memattr_to_str(mapmode));
929291031Scem	} else
930289647Scem		device_printf(ntb->device,
931289647Scem		    "Unable to mark BAR%d v:[%p-%p] p:[%p-%p] as "
932291280Scem		    "%s: %d\n",
933289647Scem		    PCI_RID2BAR(bar->pci_resource_id), bar->vbase,
934289647Scem		    (char *)bar->vbase + bar->size - 1,
935289647Scem		    (void *)bar->pbase, (void *)(bar->pbase + bar->size - 1),
936295487Scem		    ntb_vm_memattr_to_str(mapmode), rc);
937289647Scem		/* Proceed anyway */
938250079Scarl	return (0);
939250079Scarl}
940250079Scarl
941250079Scarlstatic void
942250079Scarlntb_unmap_pci_bar(struct ntb_softc *ntb)
943250079Scarl{
944250079Scarl	struct ntb_pci_bar_info *current_bar;
945250079Scarl	int i;
946250079Scarl
947289397Scem	for (i = 0; i < NTB_MAX_BARS; i++) {
948250079Scarl		current_bar = &ntb->bar_info[i];
949250079Scarl		if (current_bar->pci_resource != NULL)
950250079Scarl			bus_release_resource(ntb->device, SYS_RES_MEMORY,
951250079Scarl			    current_bar->pci_resource_id,
952250079Scarl			    current_bar->pci_resource);
953250079Scarl	}
954250079Scarl}
955250079Scarl
956250079Scarlstatic int
957289540Scemntb_setup_msix(struct ntb_softc *ntb, uint32_t num_vectors)
958250079Scarl{
959289342Scem	uint32_t i;
960289342Scem	int rc;
961289342Scem
962289342Scem	for (i = 0; i < num_vectors; i++) {
963289342Scem		ntb->int_info[i].rid = i + 1;
964289342Scem		ntb->int_info[i].res = bus_alloc_resource_any(ntb->device,
965289342Scem		    SYS_RES_IRQ, &ntb->int_info[i].rid, RF_ACTIVE);
966289342Scem		if (ntb->int_info[i].res == NULL) {
967289342Scem			device_printf(ntb->device,
968289342Scem			    "bus_alloc_resource failed\n");
969289342Scem			return (ENOMEM);
970289342Scem		}
971289342Scem		ntb->int_info[i].tag = NULL;
972289342Scem		ntb->allocated_interrupts++;
973289342Scem		rc = bus_setup_intr(ntb->device, ntb->int_info[i].res,
974289546Scem		    INTR_MPSAFE | INTR_TYPE_MISC, NULL, ndev_vec_isr,
975289546Scem		    &ntb->msix_vec[i], &ntb->int_info[i].tag);
976289342Scem		if (rc != 0) {
977289342Scem			device_printf(ntb->device, "bus_setup_intr failed\n");
978289342Scem			return (ENXIO);
979289342Scem		}
980289342Scem	}
981289342Scem	return (0);
982289342Scem}
983289342Scem
984289344Scem/*
985289344Scem * The Linux NTB driver drops from MSI-X to legacy INTx if a unique vector
986289344Scem * cannot be allocated for each MSI-X message.  JHB seems to think remapping
987289344Scem * should be okay.  This tunable should enable us to test that hypothesis
988289344Scem * when someone gets their hands on some Xeon hardware.
989289344Scem */
990289344Scemstatic int ntb_force_remap_mode;
991289344ScemSYSCTL_INT(_hw_ntb, OID_AUTO, force_remap_mode, CTLFLAG_RDTUN,
992289344Scem    &ntb_force_remap_mode, 0, "If enabled, force MSI-X messages to be remapped"
993289344Scem    " to a smaller number of ithreads, even if the desired number are "
994289344Scem    "available");
995289344Scem
996289344Scem/*
997289344Scem * In case it is NOT ok, give consumers an abort button.
998289344Scem */
999289344Scemstatic int ntb_prefer_intx;
1000289344ScemSYSCTL_INT(_hw_ntb, OID_AUTO, prefer_intx_to_remap, CTLFLAG_RDTUN,
1001289344Scem    &ntb_prefer_intx, 0, "If enabled, prefer to use legacy INTx mode rather "
1002289344Scem    "than remapping MSI-X messages over available slots (match Linux driver "
1003289344Scem    "behavior)");
1004289344Scem
1005289344Scem/*
1006289344Scem * Remap the desired number of MSI-X messages to available ithreads in a simple
1007289344Scem * round-robin fashion.
1008289344Scem */
1009289342Scemstatic int
1010289344Scemntb_remap_msix(device_t dev, uint32_t desired, uint32_t avail)
1011289344Scem{
1012289344Scem	u_int *vectors;
1013289344Scem	uint32_t i;
1014289344Scem	int rc;
1015289344Scem
1016289344Scem	if (ntb_prefer_intx != 0)
1017289344Scem		return (ENXIO);
1018289344Scem
1019289344Scem	vectors = malloc(desired * sizeof(*vectors), M_NTB, M_ZERO | M_WAITOK);
1020289344Scem
1021289344Scem	for (i = 0; i < desired; i++)
1022289344Scem		vectors[i] = (i % avail) + 1;
1023289344Scem
1024289344Scem	rc = pci_remap_msix(dev, desired, vectors);
1025289344Scem	free(vectors, M_NTB);
1026289344Scem	return (rc);
1027289344Scem}
1028289344Scem
1029289344Scemstatic int
1030289540Scemntb_init_isr(struct ntb_softc *ntb)
1031289342Scem{
1032289344Scem	uint32_t desired_vectors, num_vectors;
1033289342Scem	int rc;
1034250079Scarl
1035250079Scarl	ntb->allocated_interrupts = 0;
1036289542Scem	ntb->last_ts = ticks;
1037289347Scem
1038250079Scarl	/*
1039295618Scem	 * Mask all doorbell interrupts.  (Except link events!)
1040250079Scarl	 */
1041295618Scem	DB_MASK_LOCK(ntb);
1042295618Scem	ntb->db_mask = ntb->db_valid_mask;
1043295618Scem	db_iowrite(ntb, ntb->self_reg->db_mask, ntb->db_mask);
1044295618Scem	DB_MASK_UNLOCK(ntb);
1045250079Scarl
1046289344Scem	num_vectors = desired_vectors = MIN(pci_msix_count(ntb->device),
1047289539Scem	    ntb->db_count);
1048289344Scem	if (desired_vectors >= 1) {
1049289344Scem		rc = pci_alloc_msix(ntb->device, &num_vectors);
1050250079Scarl
1051289344Scem		if (ntb_force_remap_mode != 0 && rc == 0 &&
1052289344Scem		    num_vectors == desired_vectors)
1053289344Scem			num_vectors--;
1054289344Scem
1055289344Scem		if (rc == 0 && num_vectors < desired_vectors) {
1056289344Scem			rc = ntb_remap_msix(ntb->device, desired_vectors,
1057289344Scem			    num_vectors);
1058289344Scem			if (rc == 0)
1059289344Scem				num_vectors = desired_vectors;
1060289344Scem			else
1061289344Scem				pci_release_msi(ntb->device);
1062289344Scem		}
1063289344Scem		if (rc != 0)
1064289344Scem			num_vectors = 1;
1065289344Scem	} else
1066289344Scem		num_vectors = 1;
1067289344Scem
1068289539Scem	if (ntb->type == NTB_XEON && num_vectors < ntb->db_vec_count) {
1069302484Smav		if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP)) {
1070295618Scem			device_printf(ntb->device,
1071295618Scem			    "Errata workaround does not support MSI or INTX\n");
1072295618Scem			return (EINVAL);
1073295618Scem		}
1074295618Scem
1075289539Scem		ntb->db_vec_count = 1;
1076290680Scem		ntb->db_vec_shift = XEON_DB_TOTAL_SHIFT;
1077289539Scem		rc = ntb_setup_legacy_interrupt(ntb);
1078289539Scem	} else {
1079300531Scem		if (num_vectors - 1 != XEON_NONLINK_DB_MSIX_BITS &&
1080302484Smav		    HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP)) {
1081300531Scem			device_printf(ntb->device,
1082300531Scem			    "Errata workaround expects %d doorbell bits\n",
1083300531Scem			    XEON_NONLINK_DB_MSIX_BITS);
1084300531Scem			return (EINVAL);
1085300531Scem		}
1086300531Scem
1087289546Scem		ntb_create_msix_vec(ntb, num_vectors);
1088289540Scem		rc = ntb_setup_msix(ntb, num_vectors);
1089302484Smav		if (rc == 0 && HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP))
1090300531Scem			ntb_get_msix_info(ntb);
1091289539Scem	}
1092289539Scem	if (rc != 0) {
1093289539Scem		device_printf(ntb->device,
1094289539Scem		    "Error allocating interrupts: %d\n", rc);
1095289546Scem		ntb_free_msix_vec(ntb);
1096289396Scem	}
1097289396Scem
1098289342Scem	return (rc);
1099289342Scem}
1100289342Scem
1101289342Scemstatic int
1102289342Scemntb_setup_legacy_interrupt(struct ntb_softc *ntb)
1103289342Scem{
1104289342Scem	int rc;
1105289342Scem
1106289342Scem	ntb->int_info[0].rid = 0;
1107289342Scem	ntb->int_info[0].res = bus_alloc_resource_any(ntb->device, SYS_RES_IRQ,
1108289342Scem	    &ntb->int_info[0].rid, RF_SHAREABLE|RF_ACTIVE);
1109289342Scem	if (ntb->int_info[0].res == NULL) {
1110289342Scem		device_printf(ntb->device, "bus_alloc_resource failed\n");
1111289342Scem		return (ENOMEM);
1112250079Scarl	}
1113250079Scarl
1114289342Scem	ntb->int_info[0].tag = NULL;
1115289342Scem	ntb->allocated_interrupts = 1;
1116289342Scem
1117289342Scem	rc = bus_setup_intr(ntb->device, ntb->int_info[0].res,
1118289546Scem	    INTR_MPSAFE | INTR_TYPE_MISC, NULL, ndev_irq_isr,
1119289342Scem	    ntb, &ntb->int_info[0].tag);
1120289342Scem	if (rc != 0) {
1121289342Scem		device_printf(ntb->device, "bus_setup_intr failed\n");
1122289342Scem		return (ENXIO);
1123289342Scem	}
1124289342Scem
1125250079Scarl	return (0);
1126250079Scarl}
1127250079Scarl
1128250079Scarlstatic void
1129250079Scarlntb_teardown_interrupts(struct ntb_softc *ntb)
1130250079Scarl{
1131250079Scarl	struct ntb_int_info *current_int;
1132250079Scarl	int i;
1133250079Scarl
1134289209Scem	for (i = 0; i < ntb->allocated_interrupts; i++) {
1135250079Scarl		current_int = &ntb->int_info[i];
1136250079Scarl		if (current_int->tag != NULL)
1137250079Scarl			bus_teardown_intr(ntb->device, current_int->res,
1138250079Scarl			    current_int->tag);
1139250079Scarl
1140250079Scarl		if (current_int->res != NULL)
1141250079Scarl			bus_release_resource(ntb->device, SYS_RES_IRQ,
1142250079Scarl			    rman_get_rid(current_int->res), current_int->res);
1143250079Scarl	}
1144250079Scarl
1145289546Scem	ntb_free_msix_vec(ntb);
1146250079Scarl	pci_release_msi(ntb->device);
1147250079Scarl}
1148250079Scarl
1149289347Scem/*
1150289648Scem * Doorbell register and mask are 64-bit on Atom, 16-bit on Xeon.  Abstract it
1151289347Scem * out to make code clearer.
1152289347Scem */
1153289539Scemstatic inline uint64_t
1154289546Scemdb_ioread(struct ntb_softc *ntb, uint64_t regoff)
1155289347Scem{
1156289347Scem
1157289648Scem	if (ntb->type == NTB_ATOM)
1158289347Scem		return (ntb_reg_read(8, regoff));
1159289347Scem
1160289347Scem	KASSERT(ntb->type == NTB_XEON, ("bad ntb type"));
1161289347Scem
1162289347Scem	return (ntb_reg_read(2, regoff));
1163289347Scem}
1164289347Scem
1165289539Scemstatic inline void
1166289546Scemdb_iowrite(struct ntb_softc *ntb, uint64_t regoff, uint64_t val)
1167289347Scem{
1168289347Scem
1169289542Scem	KASSERT((val & ~ntb->db_valid_mask) == 0,
1170289542Scem	    ("%s: Invalid bits 0x%jx (valid: 0x%jx)", __func__,
1171289542Scem	     (uintmax_t)(val & ~ntb->db_valid_mask),
1172289542Scem	     (uintmax_t)ntb->db_valid_mask));
1173289542Scem
1174289607Scem	if (regoff == ntb->self_reg->db_mask)
1175289546Scem		DB_MASK_ASSERT(ntb, MA_OWNED);
1176290678Scem	db_iowrite_raw(ntb, regoff, val);
1177290678Scem}
1178289542Scem
1179290678Scemstatic inline void
1180290678Scemdb_iowrite_raw(struct ntb_softc *ntb, uint64_t regoff, uint64_t val)
1181290678Scem{
1182290678Scem
1183289648Scem	if (ntb->type == NTB_ATOM) {
1184289347Scem		ntb_reg_write(8, regoff, val);
1185289347Scem		return;
1186289347Scem	}
1187289347Scem
1188289347Scem	KASSERT(ntb->type == NTB_XEON, ("bad ntb type"));
1189289347Scem	ntb_reg_write(2, regoff, (uint16_t)val);
1190289347Scem}
1191289347Scem
1192302484Smavstatic void
1193302484Smavntb_db_set_mask(device_t dev, uint64_t bits)
1194289542Scem{
1195302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
1196289542Scem
1197289546Scem	DB_MASK_LOCK(ntb);
1198289542Scem	ntb->db_mask |= bits;
1199302493Smav	if (!HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP))
1200302493Smav		db_iowrite(ntb, ntb->self_reg->db_mask, ntb->db_mask);
1201289546Scem	DB_MASK_UNLOCK(ntb);
1202289542Scem}
1203289542Scem
1204302484Smavstatic void
1205302484Smavntb_db_clear_mask(device_t dev, uint64_t bits)
1206289542Scem{
1207302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
1208302493Smav	uint64_t ibits;
1209302493Smav	int i;
1210289542Scem
1211289542Scem	KASSERT((bits & ~ntb->db_valid_mask) == 0,
1212289542Scem	    ("%s: Invalid bits 0x%jx (valid: 0x%jx)", __func__,
1213289542Scem	     (uintmax_t)(bits & ~ntb->db_valid_mask),
1214289542Scem	     (uintmax_t)ntb->db_valid_mask));
1215289542Scem
1216289546Scem	DB_MASK_LOCK(ntb);
1217302493Smav	ibits = ntb->fake_db_bell & ntb->db_mask & bits;
1218289542Scem	ntb->db_mask &= ~bits;
1219302493Smav	if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP)) {
1220302493Smav		/* Simulate fake interrupts if unmasked DB bits are set. */
1221302493Smav		for (i = 0; i < XEON_NONLINK_DB_MSIX_BITS; i++) {
1222302493Smav			if ((ibits & ntb_db_vector_mask(dev, i)) != 0)
1223302493Smav				swi_sched(ntb->int_info[i].tag, 0);
1224302493Smav		}
1225302493Smav	} else {
1226302493Smav		db_iowrite(ntb, ntb->self_reg->db_mask, ntb->db_mask);
1227302493Smav	}
1228289546Scem	DB_MASK_UNLOCK(ntb);
1229289542Scem}
1230289542Scem
1231302484Smavstatic uint64_t
1232302484Smavntb_db_read(device_t dev)
1233289281Scem{
1234302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
1235289281Scem
1236302493Smav	if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP))
1237302493Smav		return (ntb->fake_db_bell);
1238295618Scem
1239289607Scem	return (db_ioread(ntb, ntb->self_reg->db_bell));
1240289281Scem}
1241289281Scem
1242302484Smavstatic void
1243302484Smavntb_db_clear(device_t dev, uint64_t bits)
1244289281Scem{
1245302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
1246289281Scem
1247289546Scem	KASSERT((bits & ~ntb->db_valid_mask) == 0,
1248289546Scem	    ("%s: Invalid bits 0x%jx (valid: 0x%jx)", __func__,
1249289546Scem	     (uintmax_t)(bits & ~ntb->db_valid_mask),
1250289546Scem	     (uintmax_t)ntb->db_valid_mask));
1251289546Scem
1252302484Smav	if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP)) {
1253302493Smav		DB_MASK_LOCK(ntb);
1254302493Smav		ntb->fake_db_bell &= ~bits;
1255302493Smav		DB_MASK_UNLOCK(ntb);
1256295618Scem		return;
1257295618Scem	}
1258295618Scem
1259289607Scem	db_iowrite(ntb, ntb->self_reg->db_bell, bits);
1260289281Scem}
1261289281Scem
1262289540Scemstatic inline uint64_t
1263289540Scemntb_vec_mask(struct ntb_softc *ntb, uint64_t db_vector)
1264250079Scarl{
1265289540Scem	uint64_t shift, mask;
1266250079Scarl
1267302493Smav	if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP)) {
1268302493Smav		/*
1269302493Smav		 * Remap vectors in custom way to make at least first
1270302493Smav		 * three doorbells to not generate stray events.
1271302493Smav		 * This breaks Linux compatibility (if one existed)
1272302493Smav		 * when more then one DB is used (not by if_ntb).
1273302493Smav		 */
1274302493Smav		if (db_vector < XEON_NONLINK_DB_MSIX_BITS - 1)
1275302493Smav			return (1 << db_vector);
1276302493Smav		if (db_vector == XEON_NONLINK_DB_MSIX_BITS - 1)
1277302493Smav			return (0x7ffc);
1278302493Smav	}
1279302493Smav
1280289540Scem	shift = ntb->db_vec_shift;
1281289540Scem	mask = (1ull << shift) - 1;
1282289540Scem	return (mask << (shift * db_vector));
1283250079Scarl}
1284250079Scarl
1285250079Scarlstatic void
1286289546Scemntb_interrupt(struct ntb_softc *ntb, uint32_t vec)
1287250079Scarl{
1288289540Scem	uint64_t vec_mask;
1289250079Scarl
1290289542Scem	ntb->last_ts = ticks;
1291289546Scem	vec_mask = ntb_vec_mask(ntb, vec);
1292250079Scarl
1293289542Scem	if ((vec_mask & ntb->db_link_mask) != 0) {
1294289546Scem		if (ntb_poll_link(ntb))
1295302484Smav			ntb_link_event(ntb->device);
1296289540Scem	}
1297289540Scem
1298302484Smav	if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP) &&
1299295618Scem	    (vec_mask & ntb->db_link_mask) == 0) {
1300295618Scem		DB_MASK_LOCK(ntb);
1301302493Smav
1302302493Smav		/* Do not report same DB events again if not cleared yet. */
1303302493Smav		vec_mask &= ~ntb->fake_db_bell;
1304302493Smav
1305302493Smav		/* Update our internal doorbell register. */
1306302493Smav		ntb->fake_db_bell |= vec_mask;
1307302493Smav
1308302493Smav		/* Do not report masked DB events. */
1309302493Smav		vec_mask &= ~ntb->db_mask;
1310302493Smav
1311295618Scem		DB_MASK_UNLOCK(ntb);
1312295618Scem	}
1313295618Scem
1314289546Scem	if ((vec_mask & ntb->db_valid_mask) != 0)
1315302484Smav		ntb_db_event(ntb->device, vec);
1316289546Scem}
1317250079Scarl
1318289546Scemstatic void
1319289546Scemndev_vec_isr(void *arg)
1320289546Scem{
1321289546Scem	struct ntb_vec *nvec = arg;
1322250079Scarl
1323289546Scem	ntb_interrupt(nvec->ntb, nvec->num);
1324250079Scarl}
1325250079Scarl
1326250079Scarlstatic void
1327289546Scemndev_irq_isr(void *arg)
1328250079Scarl{
1329289546Scem	/* If we couldn't set up MSI-X, we only have the one vector. */
1330289546Scem	ntb_interrupt(arg, 0);
1331250079Scarl}
1332250079Scarl
1333250079Scarlstatic int
1334289546Scemntb_create_msix_vec(struct ntb_softc *ntb, uint32_t num_vectors)
1335250079Scarl{
1336289342Scem	uint32_t i;
1337250079Scarl
1338289546Scem	ntb->msix_vec = malloc(num_vectors * sizeof(*ntb->msix_vec), M_NTB,
1339250079Scarl	    M_ZERO | M_WAITOK);
1340250079Scarl	for (i = 0; i < num_vectors; i++) {
1341289546Scem		ntb->msix_vec[i].num = i;
1342289546Scem		ntb->msix_vec[i].ntb = ntb;
1343250079Scarl	}
1344250079Scarl
1345250079Scarl	return (0);
1346250079Scarl}
1347250079Scarl
1348250079Scarlstatic void
1349289546Scemntb_free_msix_vec(struct ntb_softc *ntb)
1350250079Scarl{
1351250079Scarl
1352289546Scem	if (ntb->msix_vec == NULL)
1353289539Scem		return;
1354289539Scem
1355289546Scem	free(ntb->msix_vec, M_NTB);
1356289546Scem	ntb->msix_vec = NULL;
1357250079Scarl}
1358250079Scarl
1359295618Scemstatic void
1360300531Scemntb_get_msix_info(struct ntb_softc *ntb)
1361295618Scem{
1362295618Scem	struct pci_devinfo *dinfo;
1363295618Scem	struct pcicfg_msix *msix;
1364295618Scem	uint32_t laddr, data, i, offset;
1365295618Scem
1366295618Scem	dinfo = device_get_ivars(ntb->device);
1367295618Scem	msix = &dinfo->cfg.msix;
1368295618Scem
1369300531Scem	CTASSERT(XEON_NONLINK_DB_MSIX_BITS == nitems(ntb->msix_data));
1370300531Scem
1371300531Scem	for (i = 0; i < XEON_NONLINK_DB_MSIX_BITS; i++) {
1372295618Scem		offset = msix->msix_table_offset + i * PCI_MSIX_ENTRY_SIZE;
1373295618Scem
1374295618Scem		laddr = bus_read_4(msix->msix_table_res, offset +
1375295618Scem		    PCI_MSIX_ENTRY_LOWER_ADDR);
1376301293Smav		ntb_printf(2, "local MSIX addr(%u): 0x%x\n", i, laddr);
1377295618Scem
1378295618Scem		KASSERT((laddr & MSI_INTEL_ADDR_BASE) == MSI_INTEL_ADDR_BASE,
1379295618Scem		    ("local MSIX addr 0x%x not in MSI base 0x%x", laddr,
1380295618Scem		     MSI_INTEL_ADDR_BASE));
1381301293Smav		ntb->msix_data[i].nmd_ofs = laddr;
1382295618Scem
1383295618Scem		data = bus_read_4(msix->msix_table_res, offset +
1384295618Scem		    PCI_MSIX_ENTRY_DATA);
1385295618Scem		ntb_printf(2, "local MSIX data(%u): 0x%x\n", i, data);
1386295618Scem
1387295618Scem		ntb->msix_data[i].nmd_data = data;
1388295618Scem	}
1389295618Scem}
1390295618Scem
1391250079Scarlstatic struct ntb_hw_info *
1392250079Scarlntb_get_device_info(uint32_t device_id)
1393250079Scarl{
1394250079Scarl	struct ntb_hw_info *ep = pci_ids;
1395250079Scarl
1396250079Scarl	while (ep->device_id) {
1397250079Scarl		if (ep->device_id == device_id)
1398250079Scarl			return (ep);
1399250079Scarl		++ep;
1400250079Scarl	}
1401250079Scarl	return (NULL);
1402250079Scarl}
1403250079Scarl
1404289272Scemstatic void
1405289272Scemntb_teardown_xeon(struct ntb_softc *ntb)
1406250079Scarl{
1407250079Scarl
1408289617Scem	if (ntb->reg != NULL)
1409302484Smav		ntb_link_disable(ntb->device);
1410250079Scarl}
1411250079Scarl
1412289397Scemstatic void
1413289397Scemntb_detect_max_mw(struct ntb_softc *ntb)
1414289397Scem{
1415289397Scem
1416289648Scem	if (ntb->type == NTB_ATOM) {
1417289648Scem		ntb->mw_count = ATOM_MW_COUNT;
1418289397Scem		return;
1419289397Scem	}
1420289397Scem
1421302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR))
1422289539Scem		ntb->mw_count = XEON_HSX_SPLIT_MW_COUNT;
1423289397Scem	else
1424289539Scem		ntb->mw_count = XEON_SNB_MW_COUNT;
1425289397Scem}
1426289397Scem
1427250079Scarlstatic int
1428289348Scemntb_detect_xeon(struct ntb_softc *ntb)
1429250079Scarl{
1430289348Scem	uint8_t ppd, conn_type;
1431250079Scarl
1432289348Scem	ppd = pci_read_config(ntb->device, NTB_PPD_OFFSET, 1);
1433289348Scem	ntb->ppd = ppd;
1434250079Scarl
1435289348Scem	if ((ppd & XEON_PPD_DEV_TYPE) != 0)
1436290681Scem		ntb->dev_type = NTB_DEV_DSD;
1437290681Scem	else
1438289257Scem		ntb->dev_type = NTB_DEV_USD;
1439289257Scem
1440289397Scem	if ((ppd & XEON_PPD_SPLIT_BAR) != 0)
1441289397Scem		ntb->features |= NTB_SPLIT_BAR;
1442289397Scem
1443302508Smav	if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP) &&
1444302508Smav	    !HAS_FEATURE(ntb, NTB_SPLIT_BAR)) {
1445302508Smav		device_printf(ntb->device,
1446302508Smav		    "Can not apply SB01BASE_LOCKUP workaround "
1447302508Smav		    "with split BARs disabled!\n");
1448302508Smav		device_printf(ntb->device,
1449302508Smav		    "Expect system hangs under heavy NTB traffic!\n");
1450302508Smav		ntb->features &= ~NTB_SB01BASE_LOCKUP;
1451302508Smav	}
1452302508Smav
1453295618Scem	/*
1454295618Scem	 * SDOORBELL errata workaround gets in the way of SB01BASE_LOCKUP
1455295618Scem	 * errata workaround; only do one at a time.
1456295618Scem	 */
1457302484Smav	if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP))
1458295618Scem		ntb->features &= ~NTB_SDOORBELL_LOCKUP;
1459289542Scem
1460289348Scem	conn_type = ppd & XEON_PPD_CONN_TYPE;
1461289348Scem	switch (conn_type) {
1462289348Scem	case NTB_CONN_B2B:
1463289348Scem		ntb->conn_type = conn_type;
1464289348Scem		break;
1465289348Scem	case NTB_CONN_RP:
1466289348Scem	case NTB_CONN_TRANSPARENT:
1467289348Scem	default:
1468289348Scem		device_printf(ntb->device, "Unsupported connection type: %u\n",
1469289348Scem		    (unsigned)conn_type);
1470289348Scem		return (ENXIO);
1471289348Scem	}
1472289348Scem	return (0);
1473289348Scem}
1474289348Scem
1475289348Scemstatic int
1476289648Scemntb_detect_atom(struct ntb_softc *ntb)
1477289348Scem{
1478289348Scem	uint32_t ppd, conn_type;
1479289348Scem
1480289348Scem	ppd = pci_read_config(ntb->device, NTB_PPD_OFFSET, 4);
1481289348Scem	ntb->ppd = ppd;
1482289348Scem
1483289648Scem	if ((ppd & ATOM_PPD_DEV_TYPE) != 0)
1484289348Scem		ntb->dev_type = NTB_DEV_DSD;
1485289348Scem	else
1486289348Scem		ntb->dev_type = NTB_DEV_USD;
1487289348Scem
1488289648Scem	conn_type = (ppd & ATOM_PPD_CONN_TYPE) >> 8;
1489289348Scem	switch (conn_type) {
1490289348Scem	case NTB_CONN_B2B:
1491289348Scem		ntb->conn_type = conn_type;
1492289348Scem		break;
1493289348Scem	default:
1494289348Scem		device_printf(ntb->device, "Unsupported NTB configuration\n");
1495289348Scem		return (ENXIO);
1496289348Scem	}
1497289348Scem	return (0);
1498289348Scem}
1499289348Scem
1500289348Scemstatic int
1501289542Scemntb_xeon_init_dev(struct ntb_softc *ntb)
1502289348Scem{
1503289542Scem	int rc;
1504289348Scem
1505289542Scem	ntb->spad_count		= XEON_SPAD_COUNT;
1506289542Scem	ntb->db_count		= XEON_DB_COUNT;
1507289542Scem	ntb->db_link_mask	= XEON_DB_LINK_BIT;
1508289542Scem	ntb->db_vec_count	= XEON_DB_MSIX_VECTOR_COUNT;
1509289542Scem	ntb->db_vec_shift	= XEON_DB_MSIX_VECTOR_SHIFT;
1510289257Scem
1511289542Scem	if (ntb->conn_type != NTB_CONN_B2B) {
1512250079Scarl		device_printf(ntb->device, "Connection type %d not supported\n",
1513289348Scem		    ntb->conn_type);
1514250079Scarl		return (ENXIO);
1515250079Scarl	}
1516250079Scarl
1517289542Scem	ntb->reg = &xeon_reg;
1518289607Scem	ntb->self_reg = &xeon_pri_reg;
1519289542Scem	ntb->peer_reg = &xeon_b2b_reg;
1520289542Scem	ntb->xlat_reg = &xeon_sec_xlat;
1521289542Scem
1522302484Smav	if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP)) {
1523302493Smav		ntb->fake_db_bell = 0;
1524295618Scem		ntb->msix_mw_idx = (ntb->mw_count + g_ntb_msix_idx) %
1525295618Scem		    ntb->mw_count;
1526295618Scem		ntb_printf(2, "Setting up MSIX mw idx %d means %u\n",
1527295618Scem		    g_ntb_msix_idx, ntb->msix_mw_idx);
1528295618Scem		rc = ntb_mw_set_wc_internal(ntb, ntb->msix_mw_idx,
1529295618Scem		    VM_MEMATTR_UNCACHEABLE);
1530295618Scem		KASSERT(rc == 0, ("shouldn't fail"));
1531302484Smav	} else if (HAS_FEATURE(ntb, NTB_SDOORBELL_LOCKUP)) {
1532295618Scem		/*
1533295618Scem		 * There is a Xeon hardware errata related to writes to SDOORBELL or
1534295618Scem		 * B2BDOORBELL in conjunction with inbound access to NTB MMIO space,
1535295618Scem		 * which may hang the system.  To workaround this, use a memory
1536295618Scem		 * window to access the interrupt and scratch pad registers on the
1537295618Scem		 * remote system.
1538295618Scem		 */
1539291263Scem		ntb->b2b_mw_idx = (ntb->mw_count + g_ntb_mw_idx) %
1540291263Scem		    ntb->mw_count;
1541291263Scem		ntb_printf(2, "Setting up b2b mw idx %d means %u\n",
1542291263Scem		    g_ntb_mw_idx, ntb->b2b_mw_idx);
1543295618Scem		rc = ntb_mw_set_wc_internal(ntb, ntb->b2b_mw_idx,
1544295618Scem		    VM_MEMATTR_UNCACHEABLE);
1545291263Scem		KASSERT(rc == 0, ("shouldn't fail"));
1546302484Smav	} else if (HAS_FEATURE(ntb, NTB_B2BDOORBELL_BIT14))
1547289208Scem		/*
1548289542Scem		 * HW Errata on bit 14 of b2bdoorbell register.  Writes will not be
1549289542Scem		 * mirrored to the remote system.  Shrink the number of bits by one,
1550289542Scem		 * since bit 14 is the last bit.
1551289542Scem		 *
1552289542Scem		 * On REGS_THRU_MW errata mode, we don't use the b2bdoorbell register
1553289542Scem		 * anyway.  Nor for non-B2B connection types.
1554289542Scem		 */
1555289543Scem		ntb->db_count = XEON_DB_COUNT - 1;
1556250079Scarl
1557289542Scem	ntb->db_valid_mask = (1ull << ntb->db_count) - 1;
1558250079Scarl
1559289542Scem	if (ntb->dev_type == NTB_DEV_USD)
1560289542Scem		rc = xeon_setup_b2b_mw(ntb, &xeon_b2b_dsd_addr,
1561289542Scem		    &xeon_b2b_usd_addr);
1562289542Scem	else
1563289542Scem		rc = xeon_setup_b2b_mw(ntb, &xeon_b2b_usd_addr,
1564289542Scem		    &xeon_b2b_dsd_addr);
1565289542Scem	if (rc != 0)
1566289542Scem		return (rc);
1567289271Scem
1568250079Scarl	/* Enable Bus Master and Memory Space on the secondary side */
1569290682Scem	ntb_reg_write(2, XEON_SPCICMD_OFFSET,
1570289542Scem	    PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
1571255279Scarl
1572290682Scem	/*
1573290682Scem	 * Mask all doorbell interrupts.
1574290682Scem	 */
1575295618Scem	DB_MASK_LOCK(ntb);
1576295618Scem	ntb->db_mask = ntb->db_valid_mask;
1577295618Scem	db_iowrite(ntb, ntb->self_reg->db_mask, ntb->db_mask);
1578295618Scem	DB_MASK_UNLOCK(ntb);
1579250079Scarl
1580290682Scem	rc = ntb_init_isr(ntb);
1581290682Scem	return (rc);
1582250079Scarl}
1583250079Scarl
1584250079Scarlstatic int
1585289648Scemntb_atom_init_dev(struct ntb_softc *ntb)
1586250079Scarl{
1587290682Scem	int error;
1588250079Scarl
1589289348Scem	KASSERT(ntb->conn_type == NTB_CONN_B2B,
1590289348Scem	    ("Unsupported NTB configuration (%d)\n", ntb->conn_type));
1591250079Scarl
1592289648Scem	ntb->spad_count		 = ATOM_SPAD_COUNT;
1593289648Scem	ntb->db_count		 = ATOM_DB_COUNT;
1594289648Scem	ntb->db_vec_count	 = ATOM_DB_MSIX_VECTOR_COUNT;
1595289648Scem	ntb->db_vec_shift	 = ATOM_DB_MSIX_VECTOR_SHIFT;
1596289542Scem	ntb->db_valid_mask	 = (1ull << ntb->db_count) - 1;
1597250079Scarl
1598289648Scem	ntb->reg = &atom_reg;
1599289648Scem	ntb->self_reg = &atom_pri_reg;
1600289648Scem	ntb->peer_reg = &atom_b2b_reg;
1601289648Scem	ntb->xlat_reg = &atom_sec_xlat;
1602289542Scem
1603250079Scarl	/*
1604289648Scem	 * FIXME - MSI-X bug on early Atom HW, remove once internal issue is
1605250079Scarl	 * resolved.  Mask transaction layer internal parity errors.
1606250079Scarl	 */
1607250079Scarl	pci_write_config(ntb->device, 0xFC, 0x4, 4);
1608250079Scarl
1609289648Scem	configure_atom_secondary_side_bars(ntb);
1610250079Scarl
1611250079Scarl	/* Enable Bus Master and Memory Space on the secondary side */
1612290682Scem	ntb_reg_write(2, ATOM_SPCICMD_OFFSET,
1613250079Scarl	    PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
1614289209Scem
1615290682Scem	error = ntb_init_isr(ntb);
1616290682Scem	if (error != 0)
1617290682Scem		return (error);
1618290682Scem
1619289542Scem	/* Initiate PCI-E link training */
1620302484Smav	ntb_link_enable(ntb->device, NTB_SPEED_AUTO, NTB_WIDTH_AUTO);
1621250079Scarl
1622289648Scem	callout_reset(&ntb->heartbeat_timer, 0, atom_link_hb, ntb);
1623289542Scem
1624250079Scarl	return (0);
1625250079Scarl}
1626250079Scarl
1627289648Scem/* XXX: Linux driver doesn't seem to do any of this for Atom. */
1628255279Scarlstatic void
1629289648Scemconfigure_atom_secondary_side_bars(struct ntb_softc *ntb)
1630255279Scarl{
1631255279Scarl
1632255279Scarl	if (ntb->dev_type == NTB_DEV_USD) {
1633289648Scem		ntb_reg_write(8, ATOM_PBAR2XLAT_OFFSET,
1634290725Scem		    XEON_B2B_BAR2_ADDR64);
1635289648Scem		ntb_reg_write(8, ATOM_PBAR4XLAT_OFFSET,
1636290725Scem		    XEON_B2B_BAR4_ADDR64);
1637290725Scem		ntb_reg_write(8, ATOM_MBAR23_OFFSET, XEON_B2B_BAR2_ADDR64);
1638290725Scem		ntb_reg_write(8, ATOM_MBAR45_OFFSET, XEON_B2B_BAR4_ADDR64);
1639255279Scarl	} else {
1640289648Scem		ntb_reg_write(8, ATOM_PBAR2XLAT_OFFSET,
1641290725Scem		    XEON_B2B_BAR2_ADDR64);
1642289648Scem		ntb_reg_write(8, ATOM_PBAR4XLAT_OFFSET,
1643290725Scem		    XEON_B2B_BAR4_ADDR64);
1644290725Scem		ntb_reg_write(8, ATOM_MBAR23_OFFSET, XEON_B2B_BAR2_ADDR64);
1645290725Scem		ntb_reg_write(8, ATOM_MBAR45_OFFSET, XEON_B2B_BAR4_ADDR64);
1646255279Scarl	}
1647255279Scarl}
1648255279Scarl
1649289543Scem
1650289543Scem/*
1651289543Scem * When working around Xeon SDOORBELL errata by remapping remote registers in a
1652289543Scem * MW, limit the B2B MW to half a MW.  By sharing a MW, half the shared MW
1653289543Scem * remains for use by a higher layer.
1654289543Scem *
1655289543Scem * Will only be used if working around SDOORBELL errata and the BIOS-configured
1656289543Scem * MW size is sufficiently large.
1657289543Scem */
1658289543Scemstatic unsigned int ntb_b2b_mw_share;
1659289543ScemSYSCTL_UINT(_hw_ntb, OID_AUTO, b2b_mw_share, CTLFLAG_RDTUN, &ntb_b2b_mw_share,
1660289543Scem    0, "If enabled (non-zero), prefer to share half of the B2B peer register "
1661289543Scem    "MW with higher level consumers.  Both sides of the NTB MUST set the same "
1662289543Scem    "value here.");
1663289543Scem
1664289543Scemstatic void
1665289543Scemxeon_reset_sbar_size(struct ntb_softc *ntb, enum ntb_bar idx,
1666289543Scem    enum ntb_bar regbar)
1667289543Scem{
1668289543Scem	struct ntb_pci_bar_info *bar;
1669289543Scem	uint8_t bar_sz;
1670289543Scem
1671302484Smav	if (!HAS_FEATURE(ntb, NTB_SPLIT_BAR) && idx >= NTB_B2B_BAR_3)
1672289543Scem		return;
1673289543Scem
1674289543Scem	bar = &ntb->bar_info[idx];
1675289543Scem	bar_sz = pci_read_config(ntb->device, bar->psz_off, 1);
1676289543Scem	if (idx == regbar) {
1677289543Scem		if (ntb->b2b_off != 0)
1678289543Scem			bar_sz--;
1679289543Scem		else
1680289543Scem			bar_sz = 0;
1681289543Scem	}
1682289543Scem	pci_write_config(ntb->device, bar->ssz_off, bar_sz, 1);
1683289543Scem	bar_sz = pci_read_config(ntb->device, bar->ssz_off, 1);
1684289543Scem	(void)bar_sz;
1685289543Scem}
1686289543Scem
1687289543Scemstatic void
1688289546Scemxeon_set_sbar_base_and_limit(struct ntb_softc *ntb, uint64_t bar_addr,
1689289543Scem    enum ntb_bar idx, enum ntb_bar regbar)
1690289543Scem{
1691301293Smav	uint64_t reg_val;
1692289546Scem	uint32_t base_reg, lmt_reg;
1693289543Scem
1694289546Scem	bar_get_xlat_params(ntb, idx, &base_reg, NULL, &lmt_reg);
1695302482Smav	if (idx == regbar) {
1696302482Smav		if (ntb->b2b_off)
1697302482Smav			bar_addr += ntb->b2b_off;
1698302482Smav		else
1699302482Smav			bar_addr = 0;
1700302482Smav	}
1701289543Scem
1702295618Scem	/*
1703295618Scem	 * Set limit registers first to avoid an errata where setting the base
1704295618Scem	 * registers locks the limit registers.
1705295618Scem	 */
1706289546Scem	if (!bar_is_64bit(ntb, idx)) {
1707301293Smav		ntb_reg_write(4, lmt_reg, bar_addr);
1708295618Scem		reg_val = ntb_reg_read(4, lmt_reg);
1709295618Scem		(void)reg_val;
1710295618Scem
1711289546Scem		ntb_reg_write(4, base_reg, bar_addr);
1712289546Scem		reg_val = ntb_reg_read(4, base_reg);
1713289546Scem		(void)reg_val;
1714295618Scem	} else {
1715301293Smav		ntb_reg_write(8, lmt_reg, bar_addr);
1716295618Scem		reg_val = ntb_reg_read(8, lmt_reg);
1717295618Scem		(void)reg_val;
1718289546Scem
1719289546Scem		ntb_reg_write(8, base_reg, bar_addr);
1720289546Scem		reg_val = ntb_reg_read(8, base_reg);
1721289546Scem		(void)reg_val;
1722289543Scem	}
1723289543Scem}
1724289543Scem
1725289543Scemstatic void
1726289543Scemxeon_set_pbar_xlat(struct ntb_softc *ntb, uint64_t base_addr, enum ntb_bar idx)
1727289543Scem{
1728289543Scem	struct ntb_pci_bar_info *bar;
1729289543Scem
1730289543Scem	bar = &ntb->bar_info[idx];
1731302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR) && idx >= NTB_B2B_BAR_2) {
1732289543Scem		ntb_reg_write(4, bar->pbarxlat_off, base_addr);
1733289543Scem		base_addr = ntb_reg_read(4, bar->pbarxlat_off);
1734289543Scem	} else {
1735289543Scem		ntb_reg_write(8, bar->pbarxlat_off, base_addr);
1736289543Scem		base_addr = ntb_reg_read(8, bar->pbarxlat_off);
1737289543Scem	}
1738289543Scem	(void)base_addr;
1739289543Scem}
1740289543Scem
1741289542Scemstatic int
1742289542Scemxeon_setup_b2b_mw(struct ntb_softc *ntb, const struct ntb_b2b_addr *addr,
1743289542Scem    const struct ntb_b2b_addr *peer_addr)
1744255279Scarl{
1745289543Scem	struct ntb_pci_bar_info *b2b_bar;
1746289543Scem	vm_size_t bar_size;
1747289543Scem	uint64_t bar_addr;
1748289543Scem	enum ntb_bar b2b_bar_num, i;
1749255279Scarl
1750289543Scem	if (ntb->b2b_mw_idx == B2B_MW_DISABLED) {
1751289543Scem		b2b_bar = NULL;
1752289543Scem		b2b_bar_num = NTB_CONFIG_BAR;
1753289543Scem		ntb->b2b_off = 0;
1754289543Scem	} else {
1755289543Scem		b2b_bar_num = ntb_mw_to_bar(ntb, ntb->b2b_mw_idx);
1756289543Scem		KASSERT(b2b_bar_num > 0 && b2b_bar_num < NTB_MAX_BARS,
1757289543Scem		    ("invalid b2b mw bar"));
1758289543Scem
1759289543Scem		b2b_bar = &ntb->bar_info[b2b_bar_num];
1760289543Scem		bar_size = b2b_bar->size;
1761289543Scem
1762289543Scem		if (ntb_b2b_mw_share != 0 &&
1763289543Scem		    (bar_size >> 1) >= XEON_B2B_MIN_SIZE)
1764289543Scem			ntb->b2b_off = bar_size >> 1;
1765289543Scem		else if (bar_size >= XEON_B2B_MIN_SIZE) {
1766289543Scem			ntb->b2b_off = 0;
1767289543Scem		} else {
1768289543Scem			device_printf(ntb->device,
1769289543Scem			    "B2B bar size is too small!\n");
1770289543Scem			return (EIO);
1771289543Scem		}
1772255279Scarl	}
1773289542Scem
1774289543Scem	/*
1775289543Scem	 * Reset the secondary bar sizes to match the primary bar sizes.
1776289543Scem	 * (Except, disable or halve the size of the B2B secondary bar.)
1777289543Scem	 */
1778289543Scem	for (i = NTB_B2B_BAR_1; i < NTB_MAX_BARS; i++)
1779289543Scem		xeon_reset_sbar_size(ntb, i, b2b_bar_num);
1780289543Scem
1781289543Scem	bar_addr = 0;
1782289543Scem	if (b2b_bar_num == NTB_CONFIG_BAR)
1783289543Scem		bar_addr = addr->bar0_addr;
1784289543Scem	else if (b2b_bar_num == NTB_B2B_BAR_1)
1785289543Scem		bar_addr = addr->bar2_addr64;
1786302484Smav	else if (b2b_bar_num == NTB_B2B_BAR_2 && !HAS_FEATURE(ntb, NTB_SPLIT_BAR))
1787289543Scem		bar_addr = addr->bar4_addr64;
1788289543Scem	else if (b2b_bar_num == NTB_B2B_BAR_2)
1789289543Scem		bar_addr = addr->bar4_addr32;
1790289543Scem	else if (b2b_bar_num == NTB_B2B_BAR_3)
1791289543Scem		bar_addr = addr->bar5_addr32;
1792289543Scem	else
1793289543Scem		KASSERT(false, ("invalid bar"));
1794289543Scem
1795289543Scem	ntb_reg_write(8, XEON_SBAR0BASE_OFFSET, bar_addr);
1796289543Scem
1797289543Scem	/*
1798289543Scem	 * Other SBARs are normally hit by the PBAR xlat, except for the b2b
1799289543Scem	 * register BAR.  The B2B BAR is either disabled above or configured
1800289543Scem	 * half-size.  It starts at PBAR xlat + offset.
1801289543Scem	 *
1802289543Scem	 * Also set up incoming BAR limits == base (zero length window).
1803289543Scem	 */
1804289543Scem	xeon_set_sbar_base_and_limit(ntb, addr->bar2_addr64, NTB_B2B_BAR_1,
1805289543Scem	    b2b_bar_num);
1806302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR)) {
1807289543Scem		xeon_set_sbar_base_and_limit(ntb, addr->bar4_addr32,
1808289543Scem		    NTB_B2B_BAR_2, b2b_bar_num);
1809289543Scem		xeon_set_sbar_base_and_limit(ntb, addr->bar5_addr32,
1810289543Scem		    NTB_B2B_BAR_3, b2b_bar_num);
1811289542Scem	} else
1812289543Scem		xeon_set_sbar_base_and_limit(ntb, addr->bar4_addr64,
1813289543Scem		    NTB_B2B_BAR_2, b2b_bar_num);
1814289543Scem
1815289543Scem	/* Zero incoming translation addrs */
1816289543Scem	ntb_reg_write(8, XEON_SBAR2XLAT_OFFSET, 0);
1817289543Scem	ntb_reg_write(8, XEON_SBAR4XLAT_OFFSET, 0);
1818289543Scem
1819302484Smav	if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP)) {
1820302510Smav		uint32_t xlat_reg, lmt_reg;
1821302493Smav		enum ntb_bar bar_num;
1822295618Scem
1823295618Scem		/*
1824295618Scem		 * We point the chosen MSIX MW BAR xlat to remote LAPIC for
1825295618Scem		 * workaround
1826295618Scem		 */
1827302510Smav		bar_num = ntb_mw_to_bar(ntb, ntb->msix_mw_idx);
1828302510Smav		bar_get_xlat_params(ntb, bar_num, NULL, &xlat_reg, &lmt_reg);
1829302510Smav		if (bar_is_64bit(ntb, bar_num)) {
1830302510Smav			ntb_reg_write(8, xlat_reg, MSI_INTEL_ADDR_BASE);
1831302510Smav			ntb->msix_xlat = ntb_reg_read(8, xlat_reg);
1832302510Smav			ntb_reg_write(8, lmt_reg, 0);
1833301293Smav		} else {
1834302510Smav			ntb_reg_write(4, xlat_reg, MSI_INTEL_ADDR_BASE);
1835302510Smav			ntb->msix_xlat = ntb_reg_read(4, xlat_reg);
1836302510Smav			ntb_reg_write(8, lmt_reg, 0);
1837301293Smav		}
1838302493Smav
1839302493Smav		ntb->peer_lapic_bar =  &ntb->bar_info[bar_num];
1840295618Scem	}
1841295618Scem	(void)ntb_reg_read(8, XEON_SBAR2XLAT_OFFSET);
1842295618Scem	(void)ntb_reg_read(8, XEON_SBAR4XLAT_OFFSET);
1843295618Scem
1844289543Scem	/* Zero outgoing translation limits (whole bar size windows) */
1845289543Scem	ntb_reg_write(8, XEON_PBAR2LMT_OFFSET, 0);
1846289543Scem	ntb_reg_write(8, XEON_PBAR4LMT_OFFSET, 0);
1847289543Scem
1848289543Scem	/* Set outgoing translation offsets */
1849289543Scem	xeon_set_pbar_xlat(ntb, peer_addr->bar2_addr64, NTB_B2B_BAR_1);
1850302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR)) {
1851289543Scem		xeon_set_pbar_xlat(ntb, peer_addr->bar4_addr32, NTB_B2B_BAR_2);
1852289543Scem		xeon_set_pbar_xlat(ntb, peer_addr->bar5_addr32, NTB_B2B_BAR_3);
1853289543Scem	} else
1854289543Scem		xeon_set_pbar_xlat(ntb, peer_addr->bar4_addr64, NTB_B2B_BAR_2);
1855289543Scem
1856289543Scem	/* Set the translation offset for B2B registers */
1857289543Scem	bar_addr = 0;
1858289543Scem	if (b2b_bar_num == NTB_CONFIG_BAR)
1859289543Scem		bar_addr = peer_addr->bar0_addr;
1860289543Scem	else if (b2b_bar_num == NTB_B2B_BAR_1)
1861289543Scem		bar_addr = peer_addr->bar2_addr64;
1862302484Smav	else if (b2b_bar_num == NTB_B2B_BAR_2 && !HAS_FEATURE(ntb, NTB_SPLIT_BAR))
1863289543Scem		bar_addr = peer_addr->bar4_addr64;
1864289543Scem	else if (b2b_bar_num == NTB_B2B_BAR_2)
1865289543Scem		bar_addr = peer_addr->bar4_addr32;
1866289543Scem	else if (b2b_bar_num == NTB_B2B_BAR_3)
1867289543Scem		bar_addr = peer_addr->bar5_addr32;
1868289543Scem	else
1869289543Scem		KASSERT(false, ("invalid bar"));
1870289543Scem
1871289543Scem	/*
1872289543Scem	 * B2B_XLAT_OFFSET is a 64-bit register but can only be written 32 bits
1873289543Scem	 * at a time.
1874289543Scem	 */
1875289543Scem	ntb_reg_write(4, XEON_B2B_XLAT_OFFSETL, bar_addr & 0xffffffff);
1876289543Scem	ntb_reg_write(4, XEON_B2B_XLAT_OFFSETU, bar_addr >> 32);
1877289542Scem	return (0);
1878255279Scarl}
1879255279Scarl
1880289546Scemstatic inline bool
1881295618Scem_xeon_link_is_up(struct ntb_softc *ntb)
1882295618Scem{
1883295618Scem
1884295618Scem	if (ntb->conn_type == NTB_CONN_TRANSPARENT)
1885295618Scem		return (true);
1886295618Scem	return ((ntb->lnk_sta & NTB_LINK_STATUS_ACTIVE) != 0);
1887295618Scem}
1888295618Scem
1889295618Scemstatic inline bool
1890289546Scemlink_is_up(struct ntb_softc *ntb)
1891289546Scem{
1892289546Scem
1893295618Scem	if (ntb->type == NTB_XEON)
1894295618Scem		return (_xeon_link_is_up(ntb) && (ntb->peer_msix_good ||
1895302484Smav		    !HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP)));
1896289546Scem
1897289648Scem	KASSERT(ntb->type == NTB_ATOM, ("ntb type"));
1898289648Scem	return ((ntb->ntb_ctl & ATOM_CNTL_LINK_DOWN) == 0);
1899289546Scem}
1900289546Scem
1901289546Scemstatic inline bool
1902289648Scematom_link_is_err(struct ntb_softc *ntb)
1903289546Scem{
1904289546Scem	uint32_t status;
1905289546Scem
1906289648Scem	KASSERT(ntb->type == NTB_ATOM, ("ntb type"));
1907289546Scem
1908289648Scem	status = ntb_reg_read(4, ATOM_LTSSMSTATEJMP_OFFSET);
1909289648Scem	if ((status & ATOM_LTSSMSTATEJMP_FORCEDETECT) != 0)
1910289546Scem		return (true);
1911289546Scem
1912289648Scem	status = ntb_reg_read(4, ATOM_IBSTERRRCRVSTS0_OFFSET);
1913289648Scem	return ((status & ATOM_IBIST_ERR_OFLOW) != 0);
1914289546Scem}
1915289546Scem
1916289648Scem/* Atom does not have link status interrupt, poll on that platform */
1917250079Scarlstatic void
1918289648Scematom_link_hb(void *arg)
1919250079Scarl{
1920250079Scarl	struct ntb_softc *ntb = arg;
1921289546Scem	sbintime_t timo, poll_ts;
1922250079Scarl
1923289546Scem	timo = NTB_HB_TIMEOUT * hz;
1924289546Scem	poll_ts = ntb->last_ts + timo;
1925289546Scem
1926289542Scem	/*
1927289542Scem	 * Delay polling the link status if an interrupt was received, unless
1928289542Scem	 * the cached link status says the link is down.
1929289542Scem	 */
1930289546Scem	if ((sbintime_t)ticks - poll_ts < 0 && link_is_up(ntb)) {
1931289546Scem		timo = poll_ts - ticks;
1932289542Scem		goto out;
1933289546Scem	}
1934289542Scem
1935289546Scem	if (ntb_poll_link(ntb))
1936302484Smav		ntb_link_event(ntb->device);
1937289542Scem
1938289648Scem	if (!link_is_up(ntb) && atom_link_is_err(ntb)) {
1939289546Scem		/* Link is down with error, proceed with recovery */
1940289648Scem		callout_reset(&ntb->lr_timer, 0, recover_atom_link, ntb);
1941289546Scem		return;
1942250079Scarl	}
1943250079Scarl
1944289542Scemout:
1945289648Scem	callout_reset(&ntb->heartbeat_timer, timo, atom_link_hb, ntb);
1946250079Scarl}
1947250079Scarl
1948250079Scarlstatic void
1949289648Scematom_perform_link_restart(struct ntb_softc *ntb)
1950250079Scarl{
1951250079Scarl	uint32_t status;
1952250079Scarl
1953250079Scarl	/* Driver resets the NTB ModPhy lanes - magic! */
1954289648Scem	ntb_reg_write(1, ATOM_MODPHY_PCSREG6, 0xe0);
1955289648Scem	ntb_reg_write(1, ATOM_MODPHY_PCSREG4, 0x40);
1956289648Scem	ntb_reg_write(1, ATOM_MODPHY_PCSREG4, 0x60);
1957289648Scem	ntb_reg_write(1, ATOM_MODPHY_PCSREG6, 0x60);
1958250079Scarl
1959250079Scarl	/* Driver waits 100ms to allow the NTB ModPhy to settle */
1960250079Scarl	pause("ModPhy", hz / 10);
1961250079Scarl
1962250079Scarl	/* Clear AER Errors, write to clear */
1963289648Scem	status = ntb_reg_read(4, ATOM_ERRCORSTS_OFFSET);
1964250079Scarl	status &= PCIM_AER_COR_REPLAY_ROLLOVER;
1965289648Scem	ntb_reg_write(4, ATOM_ERRCORSTS_OFFSET, status);
1966250079Scarl
1967250079Scarl	/* Clear unexpected electrical idle event in LTSSM, write to clear */
1968289648Scem	status = ntb_reg_read(4, ATOM_LTSSMERRSTS0_OFFSET);
1969289648Scem	status |= ATOM_LTSSMERRSTS0_UNEXPECTEDEI;
1970289648Scem	ntb_reg_write(4, ATOM_LTSSMERRSTS0_OFFSET, status);
1971250079Scarl
1972250079Scarl	/* Clear DeSkew Buffer error, write to clear */
1973289648Scem	status = ntb_reg_read(4, ATOM_DESKEWSTS_OFFSET);
1974289648Scem	status |= ATOM_DESKEWSTS_DBERR;
1975289648Scem	ntb_reg_write(4, ATOM_DESKEWSTS_OFFSET, status);
1976250079Scarl
1977289648Scem	status = ntb_reg_read(4, ATOM_IBSTERRRCRVSTS0_OFFSET);
1978289648Scem	status &= ATOM_IBIST_ERR_OFLOW;
1979289648Scem	ntb_reg_write(4, ATOM_IBSTERRRCRVSTS0_OFFSET, status);
1980250079Scarl
1981250079Scarl	/* Releases the NTB state machine to allow the link to retrain */
1982289648Scem	status = ntb_reg_read(4, ATOM_LTSSMSTATEJMP_OFFSET);
1983289648Scem	status &= ~ATOM_LTSSMSTATEJMP_FORCEDETECT;
1984289648Scem	ntb_reg_write(4, ATOM_LTSSMSTATEJMP_OFFSET, status);
1985250079Scarl}
1986250079Scarl
1987302484Smavstatic int
1988302484Smavntb_set_ctx(device_t dev, void *ctx, const struct ntb_ctx_ops *ops)
1989250079Scarl{
1990302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
1991250079Scarl
1992289546Scem	if (ctx == NULL || ops == NULL)
1993289546Scem		return (EINVAL);
1994250079Scarl
1995302491Smav	rm_wlock(&ntb->ctx_lock);
1996289546Scem	if (ntb->ctx_ops != NULL) {
1997302491Smav		rm_wunlock(&ntb->ctx_lock);
1998289546Scem		return (EINVAL);
1999250079Scarl	}
2000289546Scem	ntb->ntb_ctx = ctx;
2001289546Scem	ntb->ctx_ops = ops;
2002302491Smav	rm_wunlock(&ntb->ctx_lock);
2003250079Scarl
2004289546Scem	return (0);
2005250079Scarl}
2006250079Scarl
2007289546Scem/*
2008289546Scem * It is expected that this will only be used from contexts where the ctx_lock
2009289546Scem * is not needed to protect ntb_ctx lifetime.
2010289546Scem */
2011302484Smavstatic void *
2012302484Smavntb_get_ctx(device_t dev, const struct ntb_ctx_ops **ops)
2013289546Scem{
2014302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2015289546Scem
2016289546Scem	KASSERT(ntb->ntb_ctx != NULL && ntb->ctx_ops != NULL, ("bogus"));
2017289546Scem	if (ops != NULL)
2018289546Scem		*ops = ntb->ctx_ops;
2019289546Scem	return (ntb->ntb_ctx);
2020289546Scem}
2021289546Scem
2022302484Smavstatic void
2023302484Smavntb_clear_ctx(device_t dev)
2024289546Scem{
2025302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2026289546Scem
2027302491Smav	rm_wlock(&ntb->ctx_lock);
2028289546Scem	ntb->ntb_ctx = NULL;
2029289546Scem	ntb->ctx_ops = NULL;
2030302491Smav	rm_wunlock(&ntb->ctx_lock);
2031289546Scem}
2032289546Scem
2033289546Scem/*
2034289546Scem * ntb_link_event() - notify driver context of a change in link status
2035289546Scem * @ntb:        NTB device context
2036289546Scem *
2037289546Scem * Notify the driver context that the link status may have changed.  The driver
2038289546Scem * should call ntb_link_is_up() to get the current status.
2039289546Scem */
2040302484Smavstatic void
2041302484Smavntb_link_event(device_t dev)
2042289546Scem{
2043302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2044302491Smav	struct rm_priotracker ctx_tracker;
2045289546Scem
2046302491Smav	rm_rlock(&ntb->ctx_lock, &ctx_tracker);
2047289546Scem	if (ntb->ctx_ops != NULL && ntb->ctx_ops->link_event != NULL)
2048289546Scem		ntb->ctx_ops->link_event(ntb->ntb_ctx);
2049302491Smav	rm_runlock(&ntb->ctx_lock, &ctx_tracker);
2050289546Scem}
2051289546Scem
2052289546Scem/*
2053289546Scem * ntb_db_event() - notify driver context of a doorbell event
2054289546Scem * @ntb:        NTB device context
2055289546Scem * @vector:     Interrupt vector number
2056289546Scem *
2057289546Scem * Notify the driver context of a doorbell event.  If hardware supports
2058289546Scem * multiple interrupt vectors for doorbells, the vector number indicates which
2059289546Scem * vector received the interrupt.  The vector number is relative to the first
2060289546Scem * vector used for doorbells, starting at zero, and must be less than
2061289546Scem * ntb_db_vector_count().  The driver may call ntb_db_read() to check which
2062289546Scem * doorbell bits need service, and ntb_db_vector_mask() to determine which of
2063289546Scem * those bits are associated with the vector number.
2064289546Scem */
2065250079Scarlstatic void
2066302484Smavntb_db_event(device_t dev, uint32_t vec)
2067289272Scem{
2068302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2069302491Smav	struct rm_priotracker ctx_tracker;
2070289546Scem
2071302491Smav	rm_rlock(&ntb->ctx_lock, &ctx_tracker);
2072289546Scem	if (ntb->ctx_ops != NULL && ntb->ctx_ops->db_event != NULL)
2073289546Scem		ntb->ctx_ops->db_event(ntb->ntb_ctx, vec);
2074302491Smav	rm_runlock(&ntb->ctx_lock, &ctx_tracker);
2075289546Scem}
2076289546Scem
2077302484Smavstatic int
2078302484Smavntb_link_enable(device_t dev, enum ntb_speed speed __unused,
2079302484Smav    enum ntb_width width __unused)
2080289546Scem{
2081302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2082289280Scem	uint32_t cntl;
2083289272Scem
2084300100Scem	ntb_printf(2, "%s\n", __func__);
2085300100Scem
2086289648Scem	if (ntb->type == NTB_ATOM) {
2087289542Scem		pci_write_config(ntb->device, NTB_PPD_OFFSET,
2088289648Scem		    ntb->ppd | ATOM_PPD_INIT_LINK, 4);
2089289546Scem		return (0);
2090289542Scem	}
2091289542Scem
2092289280Scem	if (ntb->conn_type == NTB_CONN_TRANSPARENT) {
2093302484Smav		ntb_link_event(dev);
2094289546Scem		return (0);
2095289280Scem	}
2096289280Scem
2097289542Scem	cntl = ntb_reg_read(4, ntb->reg->ntb_ctl);
2098289280Scem	cntl &= ~(NTB_CNTL_LINK_DISABLE | NTB_CNTL_CFG_LOCK);
2099289280Scem	cntl |= NTB_CNTL_P2S_BAR23_SNOOP | NTB_CNTL_S2P_BAR23_SNOOP;
2100289397Scem	cntl |= NTB_CNTL_P2S_BAR4_SNOOP | NTB_CNTL_S2P_BAR4_SNOOP;
2101302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR))
2102289397Scem		cntl |= NTB_CNTL_P2S_BAR5_SNOOP | NTB_CNTL_S2P_BAR5_SNOOP;
2103289542Scem	ntb_reg_write(4, ntb->reg->ntb_ctl, cntl);
2104289546Scem	return (0);
2105289272Scem}
2106289272Scem
2107302484Smavstatic int
2108302484Smavntb_link_disable(device_t dev)
2109289272Scem{
2110302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2111289272Scem	uint32_t cntl;
2112289272Scem
2113300100Scem	ntb_printf(2, "%s\n", __func__);
2114300100Scem
2115289272Scem	if (ntb->conn_type == NTB_CONN_TRANSPARENT) {
2116302484Smav		ntb_link_event(dev);
2117289546Scem		return (0);
2118289272Scem	}
2119289272Scem
2120289542Scem	cntl = ntb_reg_read(4, ntb->reg->ntb_ctl);
2121289280Scem	cntl &= ~(NTB_CNTL_P2S_BAR23_SNOOP | NTB_CNTL_S2P_BAR23_SNOOP);
2122289397Scem	cntl &= ~(NTB_CNTL_P2S_BAR4_SNOOP | NTB_CNTL_S2P_BAR4_SNOOP);
2123302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR))
2124289397Scem		cntl &= ~(NTB_CNTL_P2S_BAR5_SNOOP | NTB_CNTL_S2P_BAR5_SNOOP);
2125289280Scem	cntl |= NTB_CNTL_LINK_DISABLE | NTB_CNTL_CFG_LOCK;
2126289542Scem	ntb_reg_write(4, ntb->reg->ntb_ctl, cntl);
2127289546Scem	return (0);
2128289272Scem}
2129289272Scem
2130302484Smavstatic bool
2131302484Smavntb_link_enabled(device_t dev)
2132300100Scem{
2133302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2134300100Scem	uint32_t cntl;
2135300100Scem
2136300100Scem	if (ntb->type == NTB_ATOM) {
2137300100Scem		cntl = pci_read_config(ntb->device, NTB_PPD_OFFSET, 4);
2138300100Scem		return ((cntl & ATOM_PPD_INIT_LINK) != 0);
2139300100Scem	}
2140300100Scem
2141300100Scem	if (ntb->conn_type == NTB_CONN_TRANSPARENT)
2142300100Scem		return (true);
2143300100Scem
2144300100Scem	cntl = ntb_reg_read(4, ntb->reg->ntb_ctl);
2145300100Scem	return ((cntl & NTB_CNTL_LINK_DISABLE) == 0);
2146300100Scem}
2147300100Scem
2148289272Scemstatic void
2149289648Scemrecover_atom_link(void *arg)
2150250079Scarl{
2151250079Scarl	struct ntb_softc *ntb = arg;
2152289608Scem	unsigned speed, width, oldspeed, oldwidth;
2153250079Scarl	uint32_t status32;
2154250079Scarl
2155289648Scem	atom_perform_link_restart(ntb);
2156250079Scarl
2157289232Scem	/*
2158289232Scem	 * There is a potential race between the 2 NTB devices recovering at
2159289232Scem	 * the same time.  If the times are the same, the link will not recover
2160289232Scem	 * and the driver will be stuck in this loop forever.  Add a random
2161289232Scem	 * interval to the recovery time to prevent this race.
2162289232Scem	 */
2163289648Scem	status32 = arc4random() % ATOM_LINK_RECOVERY_TIME;
2164289648Scem	pause("Link", (ATOM_LINK_RECOVERY_TIME + status32) * hz / 1000);
2165289232Scem
2166289648Scem	if (atom_link_is_err(ntb))
2167250079Scarl		goto retry;
2168250079Scarl
2169289542Scem	status32 = ntb_reg_read(4, ntb->reg->ntb_ctl);
2170289648Scem	if ((status32 & ATOM_CNTL_LINK_DOWN) != 0)
2171289232Scem		goto out;
2172289232Scem
2173289542Scem	status32 = ntb_reg_read(4, ntb->reg->lnk_sta);
2174289608Scem	width = NTB_LNK_STA_WIDTH(status32);
2175289608Scem	speed = status32 & NTB_LINK_SPEED_MASK;
2176289608Scem
2177289608Scem	oldwidth = NTB_LNK_STA_WIDTH(ntb->lnk_sta);
2178289608Scem	oldspeed = ntb->lnk_sta & NTB_LINK_SPEED_MASK;
2179289608Scem	if (oldwidth != width || oldspeed != speed)
2180250079Scarl		goto retry;
2181250079Scarl
2182289232Scemout:
2183289648Scem	callout_reset(&ntb->heartbeat_timer, NTB_HB_TIMEOUT * hz, atom_link_hb,
2184289542Scem	    ntb);
2185250079Scarl	return;
2186250079Scarl
2187250079Scarlretry:
2188289648Scem	callout_reset(&ntb->lr_timer, NTB_HB_TIMEOUT * hz, recover_atom_link,
2189250079Scarl	    ntb);
2190250079Scarl}
2191250079Scarl
2192289546Scem/*
2193289546Scem * Polls the HW link status register(s); returns true if something has changed.
2194289546Scem */
2195289546Scemstatic bool
2196289542Scemntb_poll_link(struct ntb_softc *ntb)
2197250079Scarl{
2198250079Scarl	uint32_t ntb_cntl;
2199289546Scem	uint16_t reg_val;
2200250079Scarl
2201289648Scem	if (ntb->type == NTB_ATOM) {
2202289542Scem		ntb_cntl = ntb_reg_read(4, ntb->reg->ntb_ctl);
2203289546Scem		if (ntb_cntl == ntb->ntb_ctl)
2204289546Scem			return (false);
2205289546Scem
2206289542Scem		ntb->ntb_ctl = ntb_cntl;
2207289542Scem		ntb->lnk_sta = ntb_reg_read(4, ntb->reg->lnk_sta);
2208250079Scarl	} else {
2209290678Scem		db_iowrite_raw(ntb, ntb->self_reg->db_bell, ntb->db_link_mask);
2210250079Scarl
2211289546Scem		reg_val = pci_read_config(ntb->device, ntb->reg->lnk_sta, 2);
2212289546Scem		if (reg_val == ntb->lnk_sta)
2213289546Scem			return (false);
2214250079Scarl
2215289546Scem		ntb->lnk_sta = reg_val;
2216295618Scem
2217302484Smav		if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP)) {
2218295618Scem			if (_xeon_link_is_up(ntb)) {
2219295618Scem				if (!ntb->peer_msix_good) {
2220295618Scem					callout_reset(&ntb->peer_msix_work, 0,
2221295618Scem					    ntb_exchange_msix, ntb);
2222295618Scem					return (false);
2223295618Scem				}
2224295618Scem			} else {
2225295618Scem				ntb->peer_msix_good = false;
2226295618Scem				ntb->peer_msix_done = false;
2227295618Scem			}
2228295618Scem		}
2229289542Scem	}
2230289546Scem	return (true);
2231289542Scem}
2232289542Scem
2233289546Scemstatic inline enum ntb_speed
2234289546Scemntb_link_sta_speed(struct ntb_softc *ntb)
2235250079Scarl{
2236250079Scarl
2237289546Scem	if (!link_is_up(ntb))
2238289546Scem		return (NTB_SPEED_NONE);
2239289546Scem	return (ntb->lnk_sta & NTB_LINK_SPEED_MASK);
2240250079Scarl}
2241250079Scarl
2242289546Scemstatic inline enum ntb_width
2243289546Scemntb_link_sta_width(struct ntb_softc *ntb)
2244250079Scarl{
2245250079Scarl
2246289546Scem	if (!link_is_up(ntb))
2247289546Scem		return (NTB_WIDTH_NONE);
2248289546Scem	return (NTB_LNK_STA_WIDTH(ntb->lnk_sta));
2249250079Scarl}
2250250079Scarl
2251289774ScemSYSCTL_NODE(_hw_ntb, OID_AUTO, debug_info, CTLFLAG_RW, 0,
2252289774Scem    "Driver state, statistics, and HW registers");
2253289774Scem
2254289774Scem#define NTB_REGSZ_MASK	(3ul << 30)
2255289774Scem#define NTB_REG_64	(1ul << 30)
2256289774Scem#define NTB_REG_32	(2ul << 30)
2257289774Scem#define NTB_REG_16	(3ul << 30)
2258289774Scem#define NTB_REG_8	(0ul << 30)
2259289774Scem
2260289774Scem#define NTB_DB_READ	(1ul << 29)
2261289774Scem#define NTB_PCI_REG	(1ul << 28)
2262289774Scem#define NTB_REGFLAGS_MASK	(NTB_REGSZ_MASK | NTB_DB_READ | NTB_PCI_REG)
2263289774Scem
2264289774Scemstatic void
2265289774Scemntb_sysctl_init(struct ntb_softc *ntb)
2266289774Scem{
2267300100Scem	struct sysctl_oid_list *globals, *tree_par, *regpar, *statpar, *errpar;
2268289774Scem	struct sysctl_ctx_list *ctx;
2269289774Scem	struct sysctl_oid *tree, *tmptree;
2270289774Scem
2271289774Scem	ctx = device_get_sysctl_ctx(ntb->device);
2272300100Scem	globals = SYSCTL_CHILDREN(device_get_sysctl_tree(ntb->device));
2273289774Scem
2274300100Scem	SYSCTL_ADD_PROC(ctx, globals, OID_AUTO, "link_status",
2275300100Scem	    CTLFLAG_RD | CTLTYPE_STRING, ntb, 0,
2276300100Scem	    sysctl_handle_link_status_human, "A",
2277300100Scem	    "Link status (human readable)");
2278300100Scem	SYSCTL_ADD_PROC(ctx, globals, OID_AUTO, "active",
2279300100Scem	    CTLFLAG_RD | CTLTYPE_UINT, ntb, 0, sysctl_handle_link_status,
2280300100Scem	    "IU", "Link status (1=active, 0=inactive)");
2281300100Scem	SYSCTL_ADD_PROC(ctx, globals, OID_AUTO, "admin_up",
2282300100Scem	    CTLFLAG_RW | CTLTYPE_UINT, ntb, 0, sysctl_handle_link_admin,
2283300100Scem	    "IU", "Set/get interface status (1=UP, 0=DOWN)");
2284300100Scem
2285300100Scem	tree = SYSCTL_ADD_NODE(ctx, globals, OID_AUTO, "debug_info",
2286300100Scem	    CTLFLAG_RD, NULL, "Driver state, statistics, and HW registers");
2287289774Scem	tree_par = SYSCTL_CHILDREN(tree);
2288289774Scem
2289289774Scem	SYSCTL_ADD_UINT(ctx, tree_par, OID_AUTO, "conn_type", CTLFLAG_RD,
2290289774Scem	    &ntb->conn_type, 0, "0 - Transparent; 1 - B2B; 2 - Root Port");
2291289774Scem	SYSCTL_ADD_UINT(ctx, tree_par, OID_AUTO, "dev_type", CTLFLAG_RD,
2292289774Scem	    &ntb->dev_type, 0, "0 - USD; 1 - DSD");
2293290687Scem	SYSCTL_ADD_UINT(ctx, tree_par, OID_AUTO, "ppd", CTLFLAG_RD,
2294290687Scem	    &ntb->ppd, 0, "Raw PPD register (cached)");
2295289774Scem
2296289774Scem	if (ntb->b2b_mw_idx != B2B_MW_DISABLED) {
2297289774Scem		SYSCTL_ADD_U8(ctx, tree_par, OID_AUTO, "b2b_idx", CTLFLAG_RD,
2298289774Scem		    &ntb->b2b_mw_idx, 0,
2299289774Scem		    "Index of the MW used for B2B remote register access");
2300289774Scem		SYSCTL_ADD_UQUAD(ctx, tree_par, OID_AUTO, "b2b_off",
2301289774Scem		    CTLFLAG_RD, &ntb->b2b_off,
2302289774Scem		    "If non-zero, offset of B2B register region in shared MW");
2303289774Scem	}
2304289774Scem
2305289774Scem	SYSCTL_ADD_PROC(ctx, tree_par, OID_AUTO, "features",
2306289774Scem	    CTLFLAG_RD | CTLTYPE_STRING, ntb, 0, sysctl_handle_features, "A",
2307289774Scem	    "Features/errata of this NTB device");
2308289774Scem
2309289774Scem	SYSCTL_ADD_UINT(ctx, tree_par, OID_AUTO, "ntb_ctl", CTLFLAG_RD,
2310290686Scem	    __DEVOLATILE(uint32_t *, &ntb->ntb_ctl), 0,
2311290686Scem	    "NTB CTL register (cached)");
2312289774Scem	SYSCTL_ADD_UINT(ctx, tree_par, OID_AUTO, "lnk_sta", CTLFLAG_RD,
2313290686Scem	    __DEVOLATILE(uint32_t *, &ntb->lnk_sta), 0,
2314290686Scem	    "LNK STA register (cached)");
2315289774Scem
2316289774Scem	SYSCTL_ADD_U8(ctx, tree_par, OID_AUTO, "mw_count", CTLFLAG_RD,
2317291263Scem	    &ntb->mw_count, 0, "MW count");
2318289774Scem	SYSCTL_ADD_U8(ctx, tree_par, OID_AUTO, "spad_count", CTLFLAG_RD,
2319289774Scem	    &ntb->spad_count, 0, "Scratchpad count");
2320289774Scem	SYSCTL_ADD_U8(ctx, tree_par, OID_AUTO, "db_count", CTLFLAG_RD,
2321289774Scem	    &ntb->db_count, 0, "Doorbell count");
2322289774Scem	SYSCTL_ADD_U8(ctx, tree_par, OID_AUTO, "db_vec_count", CTLFLAG_RD,
2323289774Scem	    &ntb->db_vec_count, 0, "Doorbell vector count");
2324289774Scem	SYSCTL_ADD_U8(ctx, tree_par, OID_AUTO, "db_vec_shift", CTLFLAG_RD,
2325289774Scem	    &ntb->db_vec_shift, 0, "Doorbell vector shift");
2326289774Scem
2327289774Scem	SYSCTL_ADD_UQUAD(ctx, tree_par, OID_AUTO, "db_valid_mask", CTLFLAG_RD,
2328289774Scem	    &ntb->db_valid_mask, "Doorbell valid mask");
2329289774Scem	SYSCTL_ADD_UQUAD(ctx, tree_par, OID_AUTO, "db_link_mask", CTLFLAG_RD,
2330289774Scem	    &ntb->db_link_mask, "Doorbell link mask");
2331289774Scem	SYSCTL_ADD_UQUAD(ctx, tree_par, OID_AUTO, "db_mask", CTLFLAG_RD,
2332289774Scem	    &ntb->db_mask, "Doorbell mask (cached)");
2333289774Scem
2334289774Scem	tmptree = SYSCTL_ADD_NODE(ctx, tree_par, OID_AUTO, "registers",
2335289774Scem	    CTLFLAG_RD, NULL, "Raw HW registers (big-endian)");
2336289774Scem	regpar = SYSCTL_CHILDREN(tmptree);
2337289774Scem
2338290682Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "ntbcntl",
2339290682Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb, NTB_REG_32 |
2340290682Scem	    ntb->reg->ntb_ctl, sysctl_handle_register, "IU",
2341290682Scem	    "NTB Control register");
2342290682Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "lnkcap",
2343290682Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb, NTB_REG_32 |
2344290682Scem	    0x19c, sysctl_handle_register, "IU",
2345290682Scem	    "NTB Link Capabilities");
2346290682Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "lnkcon",
2347290682Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb, NTB_REG_32 |
2348290682Scem	    0x1a0, sysctl_handle_register, "IU",
2349290682Scem	    "NTB Link Control register");
2350290682Scem
2351289774Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "db_mask",
2352289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2353289774Scem	    NTB_REG_64 | NTB_DB_READ | ntb->self_reg->db_mask,
2354289774Scem	    sysctl_handle_register, "QU", "Doorbell mask register");
2355289774Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "db_bell",
2356289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2357289774Scem	    NTB_REG_64 | NTB_DB_READ | ntb->self_reg->db_bell,
2358289774Scem	    sysctl_handle_register, "QU", "Doorbell register");
2359289774Scem
2360289774Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "incoming_xlat23",
2361289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2362289774Scem	    NTB_REG_64 | ntb->xlat_reg->bar2_xlat,
2363289774Scem	    sysctl_handle_register, "QU", "Incoming XLAT23 register");
2364302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR)) {
2365289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "incoming_xlat4",
2366289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2367289774Scem		    NTB_REG_32 | ntb->xlat_reg->bar4_xlat,
2368289774Scem		    sysctl_handle_register, "IU", "Incoming XLAT4 register");
2369289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "incoming_xlat5",
2370289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2371289774Scem		    NTB_REG_32 | ntb->xlat_reg->bar5_xlat,
2372289774Scem		    sysctl_handle_register, "IU", "Incoming XLAT5 register");
2373289774Scem	} else {
2374289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "incoming_xlat45",
2375289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2376289774Scem		    NTB_REG_64 | ntb->xlat_reg->bar4_xlat,
2377289774Scem		    sysctl_handle_register, "QU", "Incoming XLAT45 register");
2378289774Scem	}
2379289774Scem
2380289774Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "incoming_lmt23",
2381289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2382289774Scem	    NTB_REG_64 | ntb->xlat_reg->bar2_limit,
2383289774Scem	    sysctl_handle_register, "QU", "Incoming LMT23 register");
2384302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR)) {
2385289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "incoming_lmt4",
2386289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2387289774Scem		    NTB_REG_32 | ntb->xlat_reg->bar4_limit,
2388289774Scem		    sysctl_handle_register, "IU", "Incoming LMT4 register");
2389289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "incoming_lmt5",
2390289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2391289774Scem		    NTB_REG_32 | ntb->xlat_reg->bar5_limit,
2392289774Scem		    sysctl_handle_register, "IU", "Incoming LMT5 register");
2393289774Scem	} else {
2394289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "incoming_lmt45",
2395289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2396289774Scem		    NTB_REG_64 | ntb->xlat_reg->bar4_limit,
2397289774Scem		    sysctl_handle_register, "QU", "Incoming LMT45 register");
2398289774Scem	}
2399289774Scem
2400289774Scem	if (ntb->type == NTB_ATOM)
2401289774Scem		return;
2402289774Scem
2403289774Scem	tmptree = SYSCTL_ADD_NODE(ctx, regpar, OID_AUTO, "xeon_stats",
2404289774Scem	    CTLFLAG_RD, NULL, "Xeon HW statistics");
2405289774Scem	statpar = SYSCTL_CHILDREN(tmptree);
2406289774Scem	SYSCTL_ADD_PROC(ctx, statpar, OID_AUTO, "upstream_mem_miss",
2407289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2408289774Scem	    NTB_REG_16 | XEON_USMEMMISS_OFFSET,
2409289774Scem	    sysctl_handle_register, "SU", "Upstream Memory Miss");
2410289774Scem
2411289774Scem	tmptree = SYSCTL_ADD_NODE(ctx, regpar, OID_AUTO, "xeon_hw_err",
2412289774Scem	    CTLFLAG_RD, NULL, "Xeon HW errors");
2413289774Scem	errpar = SYSCTL_CHILDREN(tmptree);
2414289774Scem
2415290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "ppd",
2416289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2417290687Scem	    NTB_REG_8 | NTB_PCI_REG | NTB_PPD_OFFSET,
2418290687Scem	    sysctl_handle_register, "CU", "PPD");
2419290687Scem
2420290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "pbar23_sz",
2421290687Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2422290687Scem	    NTB_REG_8 | NTB_PCI_REG | XEON_PBAR23SZ_OFFSET,
2423290687Scem	    sysctl_handle_register, "CU", "PBAR23 SZ (log2)");
2424290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "pbar4_sz",
2425290687Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2426290687Scem	    NTB_REG_8 | NTB_PCI_REG | XEON_PBAR4SZ_OFFSET,
2427290687Scem	    sysctl_handle_register, "CU", "PBAR4 SZ (log2)");
2428290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "pbar5_sz",
2429290687Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2430290687Scem	    NTB_REG_8 | NTB_PCI_REG | XEON_PBAR5SZ_OFFSET,
2431290687Scem	    sysctl_handle_register, "CU", "PBAR5 SZ (log2)");
2432290687Scem
2433290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "sbar23_sz",
2434290687Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2435290687Scem	    NTB_REG_8 | NTB_PCI_REG | XEON_SBAR23SZ_OFFSET,
2436290687Scem	    sysctl_handle_register, "CU", "SBAR23 SZ (log2)");
2437290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "sbar4_sz",
2438290687Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2439290687Scem	    NTB_REG_8 | NTB_PCI_REG | XEON_SBAR4SZ_OFFSET,
2440290687Scem	    sysctl_handle_register, "CU", "SBAR4 SZ (log2)");
2441290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "sbar5_sz",
2442290687Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2443290687Scem	    NTB_REG_8 | NTB_PCI_REG | XEON_SBAR5SZ_OFFSET,
2444290687Scem	    sysctl_handle_register, "CU", "SBAR5 SZ (log2)");
2445290687Scem
2446290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "devsts",
2447290687Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2448289774Scem	    NTB_REG_16 | NTB_PCI_REG | XEON_DEVSTS_OFFSET,
2449289774Scem	    sysctl_handle_register, "SU", "DEVSTS");
2450290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "lnksts",
2451289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2452289774Scem	    NTB_REG_16 | NTB_PCI_REG | XEON_LINK_STATUS_OFFSET,
2453289774Scem	    sysctl_handle_register, "SU", "LNKSTS");
2454290687Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "slnksts",
2455290687Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2456290687Scem	    NTB_REG_16 | NTB_PCI_REG | XEON_SLINK_STATUS_OFFSET,
2457290687Scem	    sysctl_handle_register, "SU", "SLNKSTS");
2458290687Scem
2459289774Scem	SYSCTL_ADD_PROC(ctx, errpar, OID_AUTO, "uncerrsts",
2460289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2461289774Scem	    NTB_REG_32 | NTB_PCI_REG | XEON_UNCERRSTS_OFFSET,
2462289774Scem	    sysctl_handle_register, "IU", "UNCERRSTS");
2463289774Scem	SYSCTL_ADD_PROC(ctx, errpar, OID_AUTO, "corerrsts",
2464289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2465289774Scem	    NTB_REG_32 | NTB_PCI_REG | XEON_CORERRSTS_OFFSET,
2466289774Scem	    sysctl_handle_register, "IU", "CORERRSTS");
2467289774Scem
2468289774Scem	if (ntb->conn_type != NTB_CONN_B2B)
2469289774Scem		return;
2470289774Scem
2471289774Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "outgoing_xlat23",
2472289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2473289774Scem	    NTB_REG_64 | ntb->bar_info[NTB_B2B_BAR_1].pbarxlat_off,
2474289774Scem	    sysctl_handle_register, "QU", "Outgoing XLAT23 register");
2475302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR)) {
2476289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "outgoing_xlat4",
2477289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2478289774Scem		    NTB_REG_32 | ntb->bar_info[NTB_B2B_BAR_2].pbarxlat_off,
2479289774Scem		    sysctl_handle_register, "IU", "Outgoing XLAT4 register");
2480289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "outgoing_xlat5",
2481289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2482289774Scem		    NTB_REG_32 | ntb->bar_info[NTB_B2B_BAR_3].pbarxlat_off,
2483289774Scem		    sysctl_handle_register, "IU", "Outgoing XLAT5 register");
2484289774Scem	} else {
2485289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "outgoing_xlat45",
2486289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2487289774Scem		    NTB_REG_64 | ntb->bar_info[NTB_B2B_BAR_2].pbarxlat_off,
2488289774Scem		    sysctl_handle_register, "QU", "Outgoing XLAT45 register");
2489289774Scem	}
2490289774Scem
2491289774Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "outgoing_lmt23",
2492289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2493289774Scem	    NTB_REG_64 | XEON_PBAR2LMT_OFFSET,
2494289774Scem	    sysctl_handle_register, "QU", "Outgoing LMT23 register");
2495302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR)) {
2496289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "outgoing_lmt4",
2497289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2498289774Scem		    NTB_REG_32 | XEON_PBAR4LMT_OFFSET,
2499289774Scem		    sysctl_handle_register, "IU", "Outgoing LMT4 register");
2500289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "outgoing_lmt5",
2501289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2502289774Scem		    NTB_REG_32 | XEON_PBAR5LMT_OFFSET,
2503289774Scem		    sysctl_handle_register, "IU", "Outgoing LMT5 register");
2504289774Scem	} else {
2505289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "outgoing_lmt45",
2506289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2507289774Scem		    NTB_REG_64 | XEON_PBAR4LMT_OFFSET,
2508289774Scem		    sysctl_handle_register, "QU", "Outgoing LMT45 register");
2509289774Scem	}
2510289774Scem
2511289774Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "sbar01_base",
2512289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2513289774Scem	    NTB_REG_64 | ntb->xlat_reg->bar0_base,
2514289774Scem	    sysctl_handle_register, "QU", "Secondary BAR01 base register");
2515289774Scem	SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "sbar23_base",
2516289774Scem	    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2517289774Scem	    NTB_REG_64 | ntb->xlat_reg->bar2_base,
2518289774Scem	    sysctl_handle_register, "QU", "Secondary BAR23 base register");
2519302484Smav	if (HAS_FEATURE(ntb, NTB_SPLIT_BAR)) {
2520289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "sbar4_base",
2521289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2522289774Scem		    NTB_REG_32 | ntb->xlat_reg->bar4_base,
2523289774Scem		    sysctl_handle_register, "IU",
2524289774Scem		    "Secondary BAR4 base register");
2525289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "sbar5_base",
2526289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2527289774Scem		    NTB_REG_32 | ntb->xlat_reg->bar5_base,
2528289774Scem		    sysctl_handle_register, "IU",
2529289774Scem		    "Secondary BAR5 base register");
2530289774Scem	} else {
2531289774Scem		SYSCTL_ADD_PROC(ctx, regpar, OID_AUTO, "sbar45_base",
2532289774Scem		    CTLFLAG_RD | CTLTYPE_OPAQUE, ntb,
2533289774Scem		    NTB_REG_64 | ntb->xlat_reg->bar4_base,
2534289774Scem		    sysctl_handle_register, "QU",
2535289774Scem		    "Secondary BAR45 base register");
2536289774Scem	}
2537289774Scem}
2538289774Scem
2539289774Scemstatic int
2540289774Scemsysctl_handle_features(SYSCTL_HANDLER_ARGS)
2541289774Scem{
2542302483Smav	struct ntb_softc *ntb = arg1;
2543289774Scem	struct sbuf sb;
2544289774Scem	int error;
2545289774Scem
2546289774Scem	sbuf_new_for_sysctl(&sb, NULL, 256, req);
2547289774Scem
2548289774Scem	sbuf_printf(&sb, "%b", ntb->features, NTB_FEATURES_STR);
2549289774Scem	error = sbuf_finish(&sb);
2550289774Scem	sbuf_delete(&sb);
2551289774Scem
2552289774Scem	if (error || !req->newptr)
2553289774Scem		return (error);
2554289774Scem	return (EINVAL);
2555289774Scem}
2556289774Scem
2557289774Scemstatic int
2558300100Scemsysctl_handle_link_admin(SYSCTL_HANDLER_ARGS)
2559289774Scem{
2560302483Smav	struct ntb_softc *ntb = arg1;
2561300100Scem	unsigned old, new;
2562300100Scem	int error;
2563300100Scem
2564302484Smav	old = ntb_link_enabled(ntb->device);
2565300100Scem
2566300100Scem	error = SYSCTL_OUT(req, &old, sizeof(old));
2567300100Scem	if (error != 0 || req->newptr == NULL)
2568300100Scem		return (error);
2569300100Scem
2570300100Scem	error = SYSCTL_IN(req, &new, sizeof(new));
2571300100Scem	if (error != 0)
2572300100Scem		return (error);
2573300100Scem
2574300100Scem	ntb_printf(0, "Admin set interface state to '%sabled'\n",
2575300100Scem	    (new != 0)? "en" : "dis");
2576300100Scem
2577300100Scem	if (new != 0)
2578302484Smav		error = ntb_link_enable(ntb->device, NTB_SPEED_AUTO, NTB_WIDTH_AUTO);
2579300100Scem	else
2580302484Smav		error = ntb_link_disable(ntb->device);
2581300100Scem	return (error);
2582300100Scem}
2583300100Scem
2584300100Scemstatic int
2585300100Scemsysctl_handle_link_status_human(SYSCTL_HANDLER_ARGS)
2586300100Scem{
2587302483Smav	struct ntb_softc *ntb = arg1;
2588289774Scem	struct sbuf sb;
2589289774Scem	enum ntb_speed speed;
2590289774Scem	enum ntb_width width;
2591289774Scem	int error;
2592289774Scem
2593289774Scem	sbuf_new_for_sysctl(&sb, NULL, 32, req);
2594289774Scem
2595302484Smav	if (ntb_link_is_up(ntb->device, &speed, &width))
2596289774Scem		sbuf_printf(&sb, "up / PCIe Gen %u / Width x%u",
2597289774Scem		    (unsigned)speed, (unsigned)width);
2598289774Scem	else
2599289774Scem		sbuf_printf(&sb, "down");
2600289774Scem
2601289774Scem	error = sbuf_finish(&sb);
2602289774Scem	sbuf_delete(&sb);
2603289774Scem
2604289774Scem	if (error || !req->newptr)
2605289774Scem		return (error);
2606289774Scem	return (EINVAL);
2607289774Scem}
2608289774Scem
2609289774Scemstatic int
2610300100Scemsysctl_handle_link_status(SYSCTL_HANDLER_ARGS)
2611300100Scem{
2612302483Smav	struct ntb_softc *ntb = arg1;
2613300100Scem	unsigned res;
2614300100Scem	int error;
2615300100Scem
2616302484Smav	res = ntb_link_is_up(ntb->device, NULL, NULL);
2617300100Scem
2618300100Scem	error = SYSCTL_OUT(req, &res, sizeof(res));
2619300100Scem	if (error || !req->newptr)
2620300100Scem		return (error);
2621300100Scem	return (EINVAL);
2622300100Scem}
2623300100Scem
2624300100Scemstatic int
2625289774Scemsysctl_handle_register(SYSCTL_HANDLER_ARGS)
2626289774Scem{
2627289774Scem	struct ntb_softc *ntb;
2628289774Scem	const void *outp;
2629289774Scem	uintptr_t sz;
2630289774Scem	uint64_t umv;
2631289774Scem	char be[sizeof(umv)];
2632289774Scem	size_t outsz;
2633289774Scem	uint32_t reg;
2634289774Scem	bool db, pci;
2635289774Scem	int error;
2636289774Scem
2637289774Scem	ntb = arg1;
2638289774Scem	reg = arg2 & ~NTB_REGFLAGS_MASK;
2639289774Scem	sz = arg2 & NTB_REGSZ_MASK;
2640289774Scem	db = (arg2 & NTB_DB_READ) != 0;
2641289774Scem	pci = (arg2 & NTB_PCI_REG) != 0;
2642289774Scem
2643289774Scem	KASSERT(!(db && pci), ("bogus"));
2644289774Scem
2645289774Scem	if (db) {
2646289774Scem		KASSERT(sz == NTB_REG_64, ("bogus"));
2647289774Scem		umv = db_ioread(ntb, reg);
2648289774Scem		outsz = sizeof(uint64_t);
2649289774Scem	} else {
2650289774Scem		switch (sz) {
2651289774Scem		case NTB_REG_64:
2652289774Scem			if (pci)
2653289774Scem				umv = pci_read_config(ntb->device, reg, 8);
2654289774Scem			else
2655289774Scem				umv = ntb_reg_read(8, reg);
2656289774Scem			outsz = sizeof(uint64_t);
2657289774Scem			break;
2658289774Scem		case NTB_REG_32:
2659289774Scem			if (pci)
2660289774Scem				umv = pci_read_config(ntb->device, reg, 4);
2661289774Scem			else
2662289774Scem				umv = ntb_reg_read(4, reg);
2663289774Scem			outsz = sizeof(uint32_t);
2664289774Scem			break;
2665289774Scem		case NTB_REG_16:
2666289774Scem			if (pci)
2667289774Scem				umv = pci_read_config(ntb->device, reg, 2);
2668289774Scem			else
2669289774Scem				umv = ntb_reg_read(2, reg);
2670289774Scem			outsz = sizeof(uint16_t);
2671289774Scem			break;
2672289774Scem		case NTB_REG_8:
2673289774Scem			if (pci)
2674289774Scem				umv = pci_read_config(ntb->device, reg, 1);
2675289774Scem			else
2676289774Scem				umv = ntb_reg_read(1, reg);
2677289774Scem			outsz = sizeof(uint8_t);
2678289774Scem			break;
2679289774Scem		default:
2680289774Scem			panic("bogus");
2681289774Scem			break;
2682289774Scem		}
2683289774Scem	}
2684289774Scem
2685289774Scem	/* Encode bigendian so that sysctl -x is legible. */
2686289774Scem	be64enc(be, umv);
2687289774Scem	outp = ((char *)be) + sizeof(umv) - outsz;
2688289774Scem
2689289774Scem	error = SYSCTL_OUT(req, outp, outsz);
2690289774Scem	if (error || !req->newptr)
2691289774Scem		return (error);
2692289774Scem	return (EINVAL);
2693289774Scem}
2694289774Scem
2695291263Scemstatic unsigned
2696291263Scemntb_user_mw_to_idx(struct ntb_softc *ntb, unsigned uidx)
2697291263Scem{
2698291263Scem
2699295618Scem	if ((ntb->b2b_mw_idx != B2B_MW_DISABLED && ntb->b2b_off == 0 &&
2700295618Scem	    uidx >= ntb->b2b_mw_idx) ||
2701295618Scem	    (ntb->msix_mw_idx != B2B_MW_DISABLED && uidx >= ntb->msix_mw_idx))
2702295618Scem		uidx++;
2703295618Scem	if ((ntb->b2b_mw_idx != B2B_MW_DISABLED && ntb->b2b_off == 0 &&
2704295618Scem	    uidx >= ntb->b2b_mw_idx) &&
2705295618Scem	    (ntb->msix_mw_idx != B2B_MW_DISABLED && uidx >= ntb->msix_mw_idx))
2706295618Scem		uidx++;
2707291263Scem	return (uidx);
2708291263Scem}
2709291263Scem
2710295618Scemstatic void
2711295618Scemntb_exchange_msix(void *ctx)
2712295618Scem{
2713295618Scem	struct ntb_softc *ntb;
2714295618Scem	uint32_t val;
2715295618Scem	unsigned i;
2716295618Scem
2717295618Scem	ntb = ctx;
2718295618Scem
2719301292Smav	if (ntb->peer_msix_good)
2720301292Smav		goto msix_good;
2721295618Scem	if (ntb->peer_msix_done)
2722295618Scem		goto msix_done;
2723295618Scem
2724295618Scem	for (i = 0; i < XEON_NONLINK_DB_MSIX_BITS; i++) {
2725302484Smav		ntb_peer_spad_write(ntb->device, NTB_MSIX_DATA0 + i,
2726295618Scem		    ntb->msix_data[i].nmd_data);
2727302484Smav		ntb_peer_spad_write(ntb->device, NTB_MSIX_OFS0 + i,
2728301293Smav		    ntb->msix_data[i].nmd_ofs - ntb->msix_xlat);
2729295618Scem	}
2730302484Smav	ntb_peer_spad_write(ntb->device, NTB_MSIX_GUARD, NTB_MSIX_VER_GUARD);
2731295618Scem
2732302484Smav	ntb_spad_read(ntb->device, NTB_MSIX_GUARD, &val);
2733295618Scem	if (val != NTB_MSIX_VER_GUARD)
2734295618Scem		goto reschedule;
2735295618Scem
2736295618Scem	for (i = 0; i < XEON_NONLINK_DB_MSIX_BITS; i++) {
2737302484Smav		ntb_spad_read(ntb->device, NTB_MSIX_DATA0 + i, &val);
2738301293Smav		ntb_printf(2, "remote MSIX data(%u): 0x%x\n", i, val);
2739295618Scem		ntb->peer_msix_data[i].nmd_data = val;
2740302484Smav		ntb_spad_read(ntb->device, NTB_MSIX_OFS0 + i, &val);
2741301293Smav		ntb_printf(2, "remote MSIX addr(%u): 0x%x\n", i, val);
2742295618Scem		ntb->peer_msix_data[i].nmd_ofs = val;
2743295618Scem	}
2744295618Scem
2745295618Scem	ntb->peer_msix_done = true;
2746295618Scem
2747295618Scemmsix_done:
2748302484Smav	ntb_peer_spad_write(ntb->device, NTB_MSIX_DONE, NTB_MSIX_RECEIVED);
2749302484Smav	ntb_spad_read(ntb->device, NTB_MSIX_DONE, &val);
2750295618Scem	if (val != NTB_MSIX_RECEIVED)
2751295618Scem		goto reschedule;
2752295618Scem
2753295618Scem	ntb->peer_msix_good = true;
2754301292Smav	/* Give peer time to see our NTB_MSIX_RECEIVED. */
2755301292Smav	goto reschedule;
2756295618Scem
2757301292Smavmsix_good:
2758295618Scem	ntb_poll_link(ntb);
2759302484Smav	ntb_link_event(ntb->device);
2760295618Scem	return;
2761295618Scem
2762295618Scemreschedule:
2763295618Scem	ntb->lnk_sta = pci_read_config(ntb->device, ntb->reg->lnk_sta, 2);
2764301292Smav	if (_xeon_link_is_up(ntb)) {
2765301292Smav		callout_reset(&ntb->peer_msix_work,
2766301292Smav		    hz * (ntb->peer_msix_good ? 2 : 1) / 100,
2767301292Smav		    ntb_exchange_msix, ntb);
2768301292Smav	} else
2769302484Smav		ntb_spad_clear(ntb->device);
2770295618Scem}
2771295618Scem
2772289546Scem/*
2773289546Scem * Public API to the rest of the OS
2774250079Scarl */
2775250079Scarl
2776302484Smavstatic uint8_t
2777302484Smavntb_spad_count(device_t dev)
2778250079Scarl{
2779302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2780250079Scarl
2781289539Scem	return (ntb->spad_count);
2782250079Scarl}
2783250079Scarl
2784302484Smavstatic uint8_t
2785302484Smavntb_mw_count(device_t dev)
2786289396Scem{
2787302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2788295618Scem	uint8_t res;
2789289396Scem
2790295618Scem	res = ntb->mw_count;
2791291263Scem	if (ntb->b2b_mw_idx != B2B_MW_DISABLED && ntb->b2b_off == 0)
2792295618Scem		res--;
2793295618Scem	if (ntb->msix_mw_idx != B2B_MW_DISABLED)
2794295618Scem		res--;
2795295618Scem	return (res);
2796289396Scem}
2797289396Scem
2798302484Smavstatic int
2799302484Smavntb_spad_write(device_t dev, unsigned int idx, uint32_t val)
2800250079Scarl{
2801302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2802250079Scarl
2803289539Scem	if (idx >= ntb->spad_count)
2804250079Scarl		return (EINVAL);
2805250079Scarl
2806289607Scem	ntb_reg_write(4, ntb->self_reg->spad + idx * 4, val);
2807250079Scarl
2808250079Scarl	return (0);
2809250079Scarl}
2810250079Scarl
2811295618Scem/*
2812295618Scem * Zeros the local scratchpad.
2813295618Scem */
2814302484Smavstatic void
2815302484Smavntb_spad_clear(device_t dev)
2816295618Scem{
2817302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2818295618Scem	unsigned i;
2819295618Scem
2820295618Scem	for (i = 0; i < ntb->spad_count; i++)
2821302484Smav		ntb_spad_write(dev, i, 0);
2822295618Scem}
2823295618Scem
2824302484Smavstatic int
2825302484Smavntb_spad_read(device_t dev, unsigned int idx, uint32_t *val)
2826250079Scarl{
2827302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2828250079Scarl
2829289539Scem	if (idx >= ntb->spad_count)
2830250079Scarl		return (EINVAL);
2831250079Scarl
2832289607Scem	*val = ntb_reg_read(4, ntb->self_reg->spad + idx * 4);
2833250079Scarl
2834250079Scarl	return (0);
2835250079Scarl}
2836250079Scarl
2837302484Smavstatic int
2838302484Smavntb_peer_spad_write(device_t dev, unsigned int idx, uint32_t val)
2839250079Scarl{
2840302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2841250079Scarl
2842289539Scem	if (idx >= ntb->spad_count)
2843250079Scarl		return (EINVAL);
2844250079Scarl
2845302484Smav	if (HAS_FEATURE(ntb, NTB_SDOORBELL_LOCKUP))
2846290682Scem		ntb_mw_write(4, XEON_SPAD_OFFSET + idx * 4, val);
2847255279Scarl	else
2848289542Scem		ntb_reg_write(4, ntb->peer_reg->spad + idx * 4, val);
2849250079Scarl
2850250079Scarl	return (0);
2851250079Scarl}
2852250079Scarl
2853302484Smavstatic int
2854302484Smavntb_peer_spad_read(device_t dev, unsigned int idx, uint32_t *val)
2855250079Scarl{
2856302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2857250079Scarl
2858289539Scem	if (idx >= ntb->spad_count)
2859250079Scarl		return (EINVAL);
2860250079Scarl
2861302484Smav	if (HAS_FEATURE(ntb, NTB_SDOORBELL_LOCKUP))
2862290682Scem		*val = ntb_mw_read(4, XEON_SPAD_OFFSET + idx * 4);
2863255279Scarl	else
2864289542Scem		*val = ntb_reg_read(4, ntb->peer_reg->spad + idx * 4);
2865250079Scarl
2866250079Scarl	return (0);
2867250079Scarl}
2868250079Scarl
2869302484Smavstatic int
2870302484Smavntb_mw_get_range(device_t dev, unsigned mw_idx, vm_paddr_t *base,
2871291033Scem    caddr_t *vbase, size_t *size, size_t *align, size_t *align_size,
2872291033Scem    bus_addr_t *plimit)
2873250079Scarl{
2874302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2875289546Scem	struct ntb_pci_bar_info *bar;
2876291033Scem	bus_addr_t limit;
2877289546Scem	size_t bar_b2b_off;
2878291033Scem	enum ntb_bar bar_num;
2879250079Scarl
2880302484Smav	if (mw_idx >= ntb_mw_count(dev))
2881289546Scem		return (EINVAL);
2882291263Scem	mw_idx = ntb_user_mw_to_idx(ntb, mw_idx);
2883250079Scarl
2884291033Scem	bar_num = ntb_mw_to_bar(ntb, mw_idx);
2885291033Scem	bar = &ntb->bar_info[bar_num];
2886289546Scem	bar_b2b_off = 0;
2887289546Scem	if (mw_idx == ntb->b2b_mw_idx) {
2888289546Scem		KASSERT(ntb->b2b_off != 0,
2889289546Scem		    ("user shouldn't get non-shared b2b mw"));
2890289546Scem		bar_b2b_off = ntb->b2b_off;
2891289546Scem	}
2892250079Scarl
2893291033Scem	if (bar_is_64bit(ntb, bar_num))
2894291033Scem		limit = BUS_SPACE_MAXADDR;
2895291033Scem	else
2896291033Scem		limit = BUS_SPACE_MAXADDR_32BIT;
2897291033Scem
2898289546Scem	if (base != NULL)
2899289546Scem		*base = bar->pbase + bar_b2b_off;
2900289546Scem	if (vbase != NULL)
2901290679Scem		*vbase = bar->vbase + bar_b2b_off;
2902289546Scem	if (size != NULL)
2903289546Scem		*size = bar->size - bar_b2b_off;
2904289546Scem	if (align != NULL)
2905289546Scem		*align = bar->size;
2906289546Scem	if (align_size != NULL)
2907289546Scem		*align_size = 1;
2908291033Scem	if (plimit != NULL)
2909291033Scem		*plimit = limit;
2910289546Scem	return (0);
2911250079Scarl}
2912250079Scarl
2913302484Smavstatic int
2914302484Smavntb_mw_set_trans(device_t dev, unsigned idx, bus_addr_t addr, size_t size)
2915250079Scarl{
2916302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
2917289546Scem	struct ntb_pci_bar_info *bar;
2918289546Scem	uint64_t base, limit, reg_val;
2919289546Scem	size_t bar_size, mw_size;
2920289546Scem	uint32_t base_reg, xlat_reg, limit_reg;
2921289546Scem	enum ntb_bar bar_num;
2922250079Scarl
2923302484Smav	if (idx >= ntb_mw_count(dev))
2924289546Scem		return (EINVAL);
2925291263Scem	idx = ntb_user_mw_to_idx(ntb, idx);
2926250079Scarl
2927289546Scem	bar_num = ntb_mw_to_bar(ntb, idx);
2928289546Scem	bar = &ntb->bar_info[bar_num];
2929250079Scarl
2930289546Scem	bar_size = bar->size;
2931289546Scem	if (idx == ntb->b2b_mw_idx)
2932289546Scem		mw_size = bar_size - ntb->b2b_off;
2933289546Scem	else
2934289546Scem		mw_size = bar_size;
2935250079Scarl
2936289546Scem	/* Hardware requires that addr is aligned to bar size */
2937289546Scem	if ((addr & (bar_size - 1)) != 0)
2938289546Scem		return (EINVAL);
2939250079Scarl
2940289546Scem	if (size > mw_size)
2941289546Scem		return (EINVAL);
2942289546Scem
2943289546Scem	bar_get_xlat_params(ntb, bar_num, &base_reg, &xlat_reg, &limit_reg);
2944289546Scem
2945289546Scem	limit = 0;
2946289546Scem	if (bar_is_64bit(ntb, bar_num)) {
2947291032Scem		base = ntb_reg_read(8, base_reg) & BAR_HIGH_MASK;
2948289546Scem
2949289546Scem		if (limit_reg != 0 && size != mw_size)
2950289546Scem			limit = base + size;
2951289546Scem
2952289546Scem		/* Set and verify translation address */
2953289546Scem		ntb_reg_write(8, xlat_reg, addr);
2954291032Scem		reg_val = ntb_reg_read(8, xlat_reg) & BAR_HIGH_MASK;
2955289546Scem		if (reg_val != addr) {
2956289546Scem			ntb_reg_write(8, xlat_reg, 0);
2957289546Scem			return (EIO);
2958289546Scem		}
2959289546Scem
2960289546Scem		/* Set and verify the limit */
2961289546Scem		ntb_reg_write(8, limit_reg, limit);
2962291032Scem		reg_val = ntb_reg_read(8, limit_reg) & BAR_HIGH_MASK;
2963289546Scem		if (reg_val != limit) {
2964289546Scem			ntb_reg_write(8, limit_reg, base);
2965289546Scem			ntb_reg_write(8, xlat_reg, 0);
2966289546Scem			return (EIO);
2967289546Scem		}
2968289546Scem	} else {
2969289546Scem		/* Configure 32-bit (split) BAR MW */
2970289546Scem
2971291029Scem		if ((addr & UINT32_MAX) != addr)
2972291033Scem			return (ERANGE);
2973291029Scem		if (((addr + size) & UINT32_MAX) != (addr + size))
2974291033Scem			return (ERANGE);
2975289546Scem
2976291032Scem		base = ntb_reg_read(4, base_reg) & BAR_HIGH_MASK;
2977289546Scem
2978289546Scem		if (limit_reg != 0 && size != mw_size)
2979289546Scem			limit = base + size;
2980289546Scem
2981289546Scem		/* Set and verify translation address */
2982289546Scem		ntb_reg_write(4, xlat_reg, addr);
2983291032Scem		reg_val = ntb_reg_read(4, xlat_reg) & BAR_HIGH_MASK;
2984289546Scem		if (reg_val != addr) {
2985289546Scem			ntb_reg_write(4, xlat_reg, 0);
2986289546Scem			return (EIO);
2987289546Scem		}
2988289546Scem
2989289546Scem		/* Set and verify the limit */
2990289546Scem		ntb_reg_write(4, limit_reg, limit);
2991291032Scem		reg_val = ntb_reg_read(4, limit_reg) & BAR_HIGH_MASK;
2992289546Scem		if (reg_val != limit) {
2993289546Scem			ntb_reg_write(4, limit_reg, base);
2994289546Scem			ntb_reg_write(4, xlat_reg, 0);
2995289546Scem			return (EIO);
2996289546Scem		}
2997250079Scarl	}
2998289546Scem	return (0);
2999250079Scarl}
3000250079Scarl
3001302484Smavstatic int
3002302484Smavntb_mw_clear_trans(device_t dev, unsigned mw_idx)
3003289596Scem{
3004289596Scem
3005302484Smav	return (ntb_mw_set_trans(dev, mw_idx, 0, 0));
3006289596Scem}
3007289596Scem
3008302484Smavstatic int
3009302484Smavntb_mw_get_wc(device_t dev, unsigned idx, vm_memattr_t *mode)
3010291031Scem{
3011302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
3012291031Scem	struct ntb_pci_bar_info *bar;
3013291031Scem
3014302484Smav	if (idx >= ntb_mw_count(dev))
3015291031Scem		return (EINVAL);
3016291263Scem	idx = ntb_user_mw_to_idx(ntb, idx);
3017291031Scem
3018291031Scem	bar = &ntb->bar_info[ntb_mw_to_bar(ntb, idx)];
3019291280Scem	*mode = bar->map_mode;
3020291031Scem	return (0);
3021291031Scem}
3022291031Scem
3023302484Smavstatic int
3024302484Smavntb_mw_set_wc(device_t dev, unsigned idx, vm_memattr_t mode)
3025291031Scem{
3026302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
3027291263Scem
3028302484Smav	if (idx >= ntb_mw_count(dev))
3029291263Scem		return (EINVAL);
3030291263Scem
3031291263Scem	idx = ntb_user_mw_to_idx(ntb, idx);
3032291280Scem	return (ntb_mw_set_wc_internal(ntb, idx, mode));
3033291263Scem}
3034291263Scem
3035291263Scemstatic int
3036291280Scemntb_mw_set_wc_internal(struct ntb_softc *ntb, unsigned idx, vm_memattr_t mode)
3037291263Scem{
3038291031Scem	struct ntb_pci_bar_info *bar;
3039291031Scem	int rc;
3040291031Scem
3041291031Scem	bar = &ntb->bar_info[ntb_mw_to_bar(ntb, idx)];
3042291280Scem	if (bar->map_mode == mode)
3043291031Scem		return (0);
3044291031Scem
3045291280Scem	rc = pmap_change_attr((vm_offset_t)bar->vbase, bar->size, mode);
3046291031Scem	if (rc == 0)
3047291280Scem		bar->map_mode = mode;
3048291031Scem
3049291031Scem	return (rc);
3050291031Scem}
3051291031Scem
3052302484Smavstatic void
3053302484Smavntb_peer_db_set(device_t dev, uint64_t bit)
3054250079Scarl{
3055302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
3056250079Scarl
3057302484Smav	if (HAS_FEATURE(ntb, NTB_SB01BASE_LOCKUP)) {
3058295618Scem		struct ntb_pci_bar_info *lapic;
3059295618Scem		unsigned i;
3060295618Scem
3061295618Scem		lapic = ntb->peer_lapic_bar;
3062295618Scem
3063295618Scem		for (i = 0; i < XEON_NONLINK_DB_MSIX_BITS; i++) {
3064302484Smav			if ((bit & ntb_db_vector_mask(dev, i)) != 0)
3065295618Scem				bus_space_write_4(lapic->pci_bus_tag,
3066295618Scem				    lapic->pci_bus_handle,
3067295618Scem				    ntb->peer_msix_data[i].nmd_ofs,
3068295618Scem				    ntb->peer_msix_data[i].nmd_data);
3069295618Scem		}
3070295618Scem		return;
3071295618Scem	}
3072295618Scem
3073302484Smav	if (HAS_FEATURE(ntb, NTB_SDOORBELL_LOCKUP)) {
3074290682Scem		ntb_mw_write(2, XEON_PDOORBELL_OFFSET, bit);
3075289347Scem		return;
3076289209Scem	}
3077289347Scem
3078289546Scem	db_iowrite(ntb, ntb->peer_reg->db_bell, bit);
3079250079Scarl}
3080250079Scarl
3081302484Smavstatic int
3082302484Smavntb_peer_db_addr(device_t dev, bus_addr_t *db_addr, vm_size_t *db_size)
3083289542Scem{
3084302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
3085289542Scem	struct ntb_pci_bar_info *bar;
3086289542Scem	uint64_t regoff;
3087289542Scem
3088302484Smav	KASSERT((db_addr != NULL && db_size != NULL), ("must be non-NULL"));
3089289542Scem
3090302484Smav	if (!HAS_FEATURE(ntb, NTB_SDOORBELL_LOCKUP)) {
3091289542Scem		bar = &ntb->bar_info[NTB_CONFIG_BAR];
3092289542Scem		regoff = ntb->peer_reg->db_bell;
3093289542Scem	} else {
3094289543Scem		KASSERT(ntb->b2b_mw_idx != B2B_MW_DISABLED,
3095289543Scem		    ("invalid b2b idx"));
3096289542Scem
3097289542Scem		bar = &ntb->bar_info[ntb_mw_to_bar(ntb, ntb->b2b_mw_idx)];
3098290682Scem		regoff = XEON_PDOORBELL_OFFSET;
3099289542Scem	}
3100289542Scem	KASSERT(bar->pci_bus_tag != X86_BUS_SPACE_IO, ("uh oh"));
3101289542Scem
3102289542Scem	/* HACK: Specific to current x86 bus implementation. */
3103302484Smav	*db_addr = ((uint64_t)bar->pci_bus_handle + regoff);
3104302484Smav	*db_size = ntb->reg->db_size;
3105302484Smav	return (0);
3106289542Scem}
3107289542Scem
3108302484Smavstatic uint64_t
3109302484Smavntb_db_valid_mask(device_t dev)
3110289597Scem{
3111302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
3112289597Scem
3113289597Scem	return (ntb->db_valid_mask);
3114289597Scem}
3115289597Scem
3116302484Smavstatic int
3117302484Smavntb_db_vector_count(device_t dev)
3118289598Scem{
3119302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
3120289598Scem
3121302484Smav	return (ntb->db_vec_count);
3122302484Smav}
3123302484Smav
3124302484Smavstatic uint64_t
3125302484Smavntb_db_vector_mask(device_t dev, uint32_t vector)
3126302484Smav{
3127302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
3128302484Smav
3129289598Scem	if (vector > ntb->db_vec_count)
3130289598Scem		return (0);
3131289598Scem	return (ntb->db_valid_mask & ntb_vec_mask(ntb, vector));
3132289598Scem}
3133289598Scem
3134302484Smavstatic bool
3135302484Smavntb_link_is_up(device_t dev, enum ntb_speed *speed, enum ntb_width *width)
3136250079Scarl{
3137302484Smav	struct ntb_softc *ntb = device_get_softc(dev);
3138250079Scarl
3139289546Scem	if (speed != NULL)
3140289546Scem		*speed = ntb_link_sta_speed(ntb);
3141289546Scem	if (width != NULL)
3142289546Scem		*width = ntb_link_sta_width(ntb);
3143289546Scem	return (link_is_up(ntb));
3144250079Scarl}
3145250079Scarl
3146255272Scarlstatic void
3147255272Scarlsave_bar_parameters(struct ntb_pci_bar_info *bar)
3148250079Scarl{
3149255272Scarl
3150289209Scem	bar->pci_bus_tag = rman_get_bustag(bar->pci_resource);
3151289209Scem	bar->pci_bus_handle = rman_get_bushandle(bar->pci_resource);
3152289209Scem	bar->pbase = rman_get_start(bar->pci_resource);
3153289209Scem	bar->size = rman_get_size(bar->pci_resource);
3154289209Scem	bar->vbase = rman_get_virtual(bar->pci_resource);
3155250079Scarl}
3156255268Scarl
3157302484Smavstatic device_method_t ntb_intel_methods[] = {
3158302484Smav	/* Device interface */
3159302484Smav	DEVMETHOD(device_probe,     ntb_probe),
3160302484Smav	DEVMETHOD(device_attach,    ntb_attach),
3161302484Smav	DEVMETHOD(device_detach,    ntb_detach),
3162302484Smav	/* NTB interface */
3163302484Smav	DEVMETHOD(ntb_link_is_up,	ntb_link_is_up),
3164302484Smav	DEVMETHOD(ntb_link_enable,	ntb_link_enable),
3165302484Smav	DEVMETHOD(ntb_link_disable,	ntb_link_disable),
3166302484Smav	DEVMETHOD(ntb_link_enabled,	ntb_link_enabled),
3167302484Smav	DEVMETHOD(ntb_set_ctx,		ntb_set_ctx),
3168302484Smav	DEVMETHOD(ntb_get_ctx,		ntb_get_ctx),
3169302484Smav	DEVMETHOD(ntb_clear_ctx,	ntb_clear_ctx),
3170302484Smav	DEVMETHOD(ntb_mw_count,		ntb_mw_count),
3171302484Smav	DEVMETHOD(ntb_mw_get_range,	ntb_mw_get_range),
3172302484Smav	DEVMETHOD(ntb_mw_set_trans,	ntb_mw_set_trans),
3173302484Smav	DEVMETHOD(ntb_mw_clear_trans,	ntb_mw_clear_trans),
3174302484Smav	DEVMETHOD(ntb_mw_get_wc,	ntb_mw_get_wc),
3175302484Smav	DEVMETHOD(ntb_mw_set_wc,	ntb_mw_set_wc),
3176302484Smav	DEVMETHOD(ntb_spad_count,	ntb_spad_count),
3177302484Smav	DEVMETHOD(ntb_spad_clear,	ntb_spad_clear),
3178302484Smav	DEVMETHOD(ntb_spad_write,	ntb_spad_write),
3179302484Smav	DEVMETHOD(ntb_spad_read,	ntb_spad_read),
3180302484Smav	DEVMETHOD(ntb_peer_spad_write,	ntb_peer_spad_write),
3181302484Smav	DEVMETHOD(ntb_peer_spad_read,	ntb_peer_spad_read),
3182302484Smav	DEVMETHOD(ntb_db_valid_mask,	ntb_db_valid_mask),
3183302484Smav	DEVMETHOD(ntb_db_vector_count,	ntb_db_vector_count),
3184302484Smav	DEVMETHOD(ntb_db_vector_mask,	ntb_db_vector_mask),
3185302484Smav	DEVMETHOD(ntb_db_clear,		ntb_db_clear),
3186302484Smav	DEVMETHOD(ntb_db_clear_mask,	ntb_db_clear_mask),
3187302484Smav	DEVMETHOD(ntb_db_read,		ntb_db_read),
3188302484Smav	DEVMETHOD(ntb_db_set_mask,	ntb_db_set_mask),
3189302484Smav	DEVMETHOD(ntb_peer_db_addr,	ntb_peer_db_addr),
3190302484Smav	DEVMETHOD(ntb_peer_db_set,	ntb_peer_db_set),
3191302484Smav	DEVMETHOD_END
3192302484Smav};
3193255268Scarl
3194302484Smavstatic DEFINE_CLASS_0(ntb_hw, ntb_intel_driver, ntb_intel_methods,
3195302484Smav    sizeof(struct ntb_softc));
3196302484SmavDRIVER_MODULE(ntb_intel, pci, ntb_intel_driver, ntb_hw_devclass, NULL, NULL);
3197302484SmavMODULE_DEPEND(ntb_intel, ntb, 1, 1, 1);
3198302484SmavMODULE_VERSION(ntb_intel, 1);
3199