162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 1992-1997, 2000-2003 Silicon Graphics, Inc.
462306a36Sopenharmony_ci * Copyright (C) 2004 Christoph Hellwig.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Support functions for the HUB ASIC - mostly PIO mapping related.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/bitops.h>
1062306a36Sopenharmony_ci#include <linux/string.h>
1162306a36Sopenharmony_ci#include <linux/mmzone.h>
1262306a36Sopenharmony_ci#include <asm/sn/addrs.h>
1362306a36Sopenharmony_ci#include <asm/sn/arch.h>
1462306a36Sopenharmony_ci#include <asm/sn/agent.h>
1562306a36Sopenharmony_ci#include <asm/sn/io.h>
1662306a36Sopenharmony_ci#include <asm/xtalk/xtalk.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic int force_fire_and_forget = 1;
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/**
2262306a36Sopenharmony_ci * hub_pio_map	-  establish a HUB PIO mapping
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * @hub:	hub to perform PIO mapping on
2562306a36Sopenharmony_ci * @widget:	widget ID to perform PIO mapping for
2662306a36Sopenharmony_ci * @xtalk_addr: xtalk_address that needs to be mapped
2762306a36Sopenharmony_ci * @size:	size of the PIO mapping
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci **/
3062306a36Sopenharmony_ciunsigned long hub_pio_map(nasid_t nasid, xwidgetnum_t widget,
3162306a36Sopenharmony_ci			  unsigned long xtalk_addr, size_t size)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	unsigned i;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	/* use small-window mapping if possible */
3662306a36Sopenharmony_ci	if ((xtalk_addr % SWIN_SIZE) + size <= SWIN_SIZE)
3762306a36Sopenharmony_ci		return NODE_SWIN_BASE(nasid, widget) + (xtalk_addr % SWIN_SIZE);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	if ((xtalk_addr % BWIN_SIZE) + size > BWIN_SIZE) {
4062306a36Sopenharmony_ci		printk(KERN_WARNING "PIO mapping at hub %d widget %d addr 0x%lx"
4162306a36Sopenharmony_ci				" too big (%ld)\n",
4262306a36Sopenharmony_ci				nasid, widget, xtalk_addr, size);
4362306a36Sopenharmony_ci		return 0;
4462306a36Sopenharmony_ci	}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	xtalk_addr &= ~(BWIN_SIZE-1);
4762306a36Sopenharmony_ci	for (i = 0; i < HUB_NUM_BIG_WINDOW; i++) {
4862306a36Sopenharmony_ci		if (test_and_set_bit(i, hub_data(nasid)->h_bigwin_used))
4962306a36Sopenharmony_ci			continue;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci		/*
5262306a36Sopenharmony_ci		 * The code below does a PIO write to setup an ITTE entry.
5362306a36Sopenharmony_ci		 *
5462306a36Sopenharmony_ci		 * We need to prevent other CPUs from seeing our updated
5562306a36Sopenharmony_ci		 * memory shadow of the ITTE (in the piomap) until the ITTE
5662306a36Sopenharmony_ci		 * entry is actually set up; otherwise, another CPU might
5762306a36Sopenharmony_ci		 * attempt a PIO prematurely.
5862306a36Sopenharmony_ci		 *
5962306a36Sopenharmony_ci		 * Also, the only way we can know that an entry has been
6062306a36Sopenharmony_ci		 * received  by the hub and can be used by future PIO reads/
6162306a36Sopenharmony_ci		 * writes is by reading back the ITTE entry after writing it.
6262306a36Sopenharmony_ci		 *
6362306a36Sopenharmony_ci		 * For these two reasons, we PIO read back the ITTE entry
6462306a36Sopenharmony_ci		 * after we write it.
6562306a36Sopenharmony_ci		 */
6662306a36Sopenharmony_ci		IIO_ITTE_PUT(nasid, i, HUB_PIO_MAP_TO_MEM, widget, xtalk_addr);
6762306a36Sopenharmony_ci		__raw_readq(IIO_ITTE_GET(nasid, i));
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci		return NODE_BWIN_BASE(nasid, widget) + (xtalk_addr % BWIN_SIZE);
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	printk(KERN_WARNING "unable to establish PIO mapping for at"
7362306a36Sopenharmony_ci			" hub %d widget %d addr 0x%lx\n",
7462306a36Sopenharmony_ci			nasid, widget, xtalk_addr);
7562306a36Sopenharmony_ci	return 0;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/*
8062306a36Sopenharmony_ci * hub_setup_prb(nasid, prbnum, credits, conveyor)
8162306a36Sopenharmony_ci *
8262306a36Sopenharmony_ci *	Put a PRB into fire-and-forget mode if conveyor isn't set.  Otherwise,
8362306a36Sopenharmony_ci *	put it into conveyor belt mode with the specified number of credits.
8462306a36Sopenharmony_ci */
8562306a36Sopenharmony_cistatic void hub_setup_prb(nasid_t nasid, int prbnum, int credits)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	union iprb_u prb;
8862306a36Sopenharmony_ci	int prb_offset;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	/*
9162306a36Sopenharmony_ci	 * Get the current register value.
9262306a36Sopenharmony_ci	 */
9362306a36Sopenharmony_ci	prb_offset = IIO_IOPRB(prbnum);
9462306a36Sopenharmony_ci	prb.iprb_regval = REMOTE_HUB_L(nasid, prb_offset);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	/*
9762306a36Sopenharmony_ci	 * Clear out some fields.
9862306a36Sopenharmony_ci	 */
9962306a36Sopenharmony_ci	prb.iprb_ovflow = 1;
10062306a36Sopenharmony_ci	prb.iprb_bnakctr = 0;
10162306a36Sopenharmony_ci	prb.iprb_anakctr = 0;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	/*
10462306a36Sopenharmony_ci	 * Enable or disable fire-and-forget mode.
10562306a36Sopenharmony_ci	 */
10662306a36Sopenharmony_ci	prb.iprb_ff = force_fire_and_forget ? 1 : 0;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	/*
10962306a36Sopenharmony_ci	 * Set the appropriate number of PIO credits for the widget.
11062306a36Sopenharmony_ci	 */
11162306a36Sopenharmony_ci	prb.iprb_xtalkctr = credits;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	/*
11462306a36Sopenharmony_ci	 * Store the new value to the register.
11562306a36Sopenharmony_ci	 */
11662306a36Sopenharmony_ci	REMOTE_HUB_S(nasid, prb_offset, prb.iprb_regval);
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci/**
12062306a36Sopenharmony_ci * hub_set_piomode  -  set pio mode for a given hub
12162306a36Sopenharmony_ci *
12262306a36Sopenharmony_ci * @nasid:	physical node ID for the hub in question
12362306a36Sopenharmony_ci *
12462306a36Sopenharmony_ci * Put the hub into either "PIO conveyor belt" mode or "fire-and-forget" mode.
12562306a36Sopenharmony_ci * To do this, we have to make absolutely sure that no PIOs are in progress
12662306a36Sopenharmony_ci * so we turn off access to all widgets for the duration of the function.
12762306a36Sopenharmony_ci *
12862306a36Sopenharmony_ci * XXX - This code should really check what kind of widget we're talking
12962306a36Sopenharmony_ci * to.	Bridges can only handle three requests, but XG will do more.
13062306a36Sopenharmony_ci * How many can crossbow handle to widget 0?  We're assuming 1.
13162306a36Sopenharmony_ci *
13262306a36Sopenharmony_ci * XXX - There is a bug in the crossbow that link reset PIOs do not
13362306a36Sopenharmony_ci * return write responses.  The easiest solution to this problem is to
13462306a36Sopenharmony_ci * leave widget 0 (xbow) in fire-and-forget mode at all times.	This
13562306a36Sopenharmony_ci * only affects pio's to xbow registers, which should be rare.
13662306a36Sopenharmony_ci **/
13762306a36Sopenharmony_cistatic void hub_set_piomode(nasid_t nasid)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	u64 ii_iowa;
14062306a36Sopenharmony_ci	union hubii_wcr_u ii_wcr;
14162306a36Sopenharmony_ci	unsigned i;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	ii_iowa = REMOTE_HUB_L(nasid, IIO_OUTWIDGET_ACCESS);
14462306a36Sopenharmony_ci	REMOTE_HUB_S(nasid, IIO_OUTWIDGET_ACCESS, 0);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	ii_wcr.wcr_reg_value = REMOTE_HUB_L(nasid, IIO_WCR);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (ii_wcr.iwcr_dir_con) {
14962306a36Sopenharmony_ci		/*
15062306a36Sopenharmony_ci		 * Assume a bridge here.
15162306a36Sopenharmony_ci		 */
15262306a36Sopenharmony_ci		hub_setup_prb(nasid, 0, 3);
15362306a36Sopenharmony_ci	} else {
15462306a36Sopenharmony_ci		/*
15562306a36Sopenharmony_ci		 * Assume a crossbow here.
15662306a36Sopenharmony_ci		 */
15762306a36Sopenharmony_ci		hub_setup_prb(nasid, 0, 1);
15862306a36Sopenharmony_ci	}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/*
16162306a36Sopenharmony_ci	 * XXX - Here's where we should take the widget type into
16262306a36Sopenharmony_ci	 * when account assigning credits.
16362306a36Sopenharmony_ci	 */
16462306a36Sopenharmony_ci	for (i = HUB_WIDGET_ID_MIN; i <= HUB_WIDGET_ID_MAX; i++)
16562306a36Sopenharmony_ci		hub_setup_prb(nasid, i, 3);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	REMOTE_HUB_S(nasid, IIO_OUTWIDGET_ACCESS, ii_iowa);
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci/*
17162306a36Sopenharmony_ci * hub_pio_init	 -  PIO-related hub initialization
17262306a36Sopenharmony_ci *
17362306a36Sopenharmony_ci * @hub:	hubinfo structure for our hub
17462306a36Sopenharmony_ci */
17562306a36Sopenharmony_civoid hub_pio_init(nasid_t nasid)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	unsigned i;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	/* initialize big window piomaps for this hub */
18062306a36Sopenharmony_ci	bitmap_zero(hub_data(nasid)->h_bigwin_used, HUB_NUM_BIG_WINDOW);
18162306a36Sopenharmony_ci	for (i = 0; i < HUB_NUM_BIG_WINDOW; i++)
18262306a36Sopenharmony_ci		IIO_ITTE_DISABLE(nasid, i);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	hub_set_piomode(nasid);
18562306a36Sopenharmony_ci}
186