18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 1992-1997, 2000-2003 Silicon Graphics, Inc.
48c2ecf20Sopenharmony_ci * Copyright (C) 2004 Christoph Hellwig.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Support functions for the HUB ASIC - mostly PIO mapping related.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/bitops.h>
108c2ecf20Sopenharmony_ci#include <linux/string.h>
118c2ecf20Sopenharmony_ci#include <linux/mmzone.h>
128c2ecf20Sopenharmony_ci#include <asm/sn/addrs.h>
138c2ecf20Sopenharmony_ci#include <asm/sn/arch.h>
148c2ecf20Sopenharmony_ci#include <asm/sn/agent.h>
158c2ecf20Sopenharmony_ci#include <asm/sn/io.h>
168c2ecf20Sopenharmony_ci#include <asm/xtalk/xtalk.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic int force_fire_and_forget = 1;
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/**
228c2ecf20Sopenharmony_ci * hub_pio_map	-  establish a HUB PIO mapping
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * @hub:	hub to perform PIO mapping on
258c2ecf20Sopenharmony_ci * @widget:	widget ID to perform PIO mapping for
268c2ecf20Sopenharmony_ci * @xtalk_addr: xtalk_address that needs to be mapped
278c2ecf20Sopenharmony_ci * @size:	size of the PIO mapping
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci **/
308c2ecf20Sopenharmony_ciunsigned long hub_pio_map(nasid_t nasid, xwidgetnum_t widget,
318c2ecf20Sopenharmony_ci			  unsigned long xtalk_addr, size_t size)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	unsigned i;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	/* use small-window mapping if possible */
368c2ecf20Sopenharmony_ci	if ((xtalk_addr % SWIN_SIZE) + size <= SWIN_SIZE)
378c2ecf20Sopenharmony_ci		return NODE_SWIN_BASE(nasid, widget) + (xtalk_addr % SWIN_SIZE);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	if ((xtalk_addr % BWIN_SIZE) + size > BWIN_SIZE) {
408c2ecf20Sopenharmony_ci		printk(KERN_WARNING "PIO mapping at hub %d widget %d addr 0x%lx"
418c2ecf20Sopenharmony_ci				" too big (%ld)\n",
428c2ecf20Sopenharmony_ci				nasid, widget, xtalk_addr, size);
438c2ecf20Sopenharmony_ci		return 0;
448c2ecf20Sopenharmony_ci	}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	xtalk_addr &= ~(BWIN_SIZE-1);
478c2ecf20Sopenharmony_ci	for (i = 0; i < HUB_NUM_BIG_WINDOW; i++) {
488c2ecf20Sopenharmony_ci		if (test_and_set_bit(i, hub_data(nasid)->h_bigwin_used))
498c2ecf20Sopenharmony_ci			continue;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci		/*
528c2ecf20Sopenharmony_ci		 * The code below does a PIO write to setup an ITTE entry.
538c2ecf20Sopenharmony_ci		 *
548c2ecf20Sopenharmony_ci		 * We need to prevent other CPUs from seeing our updated
558c2ecf20Sopenharmony_ci		 * memory shadow of the ITTE (in the piomap) until the ITTE
568c2ecf20Sopenharmony_ci		 * entry is actually set up; otherwise, another CPU might
578c2ecf20Sopenharmony_ci		 * attempt a PIO prematurely.
588c2ecf20Sopenharmony_ci		 *
598c2ecf20Sopenharmony_ci		 * Also, the only way we can know that an entry has been
608c2ecf20Sopenharmony_ci		 * received  by the hub and can be used by future PIO reads/
618c2ecf20Sopenharmony_ci		 * writes is by reading back the ITTE entry after writing it.
628c2ecf20Sopenharmony_ci		 *
638c2ecf20Sopenharmony_ci		 * For these two reasons, we PIO read back the ITTE entry
648c2ecf20Sopenharmony_ci		 * after we write it.
658c2ecf20Sopenharmony_ci		 */
668c2ecf20Sopenharmony_ci		IIO_ITTE_PUT(nasid, i, HUB_PIO_MAP_TO_MEM, widget, xtalk_addr);
678c2ecf20Sopenharmony_ci		__raw_readq(IIO_ITTE_GET(nasid, i));
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci		return NODE_BWIN_BASE(nasid, widget) + (xtalk_addr % BWIN_SIZE);
708c2ecf20Sopenharmony_ci	}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	printk(KERN_WARNING "unable to establish PIO mapping for at"
738c2ecf20Sopenharmony_ci			" hub %d widget %d addr 0x%lx\n",
748c2ecf20Sopenharmony_ci			nasid, widget, xtalk_addr);
758c2ecf20Sopenharmony_ci	return 0;
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci/*
808c2ecf20Sopenharmony_ci * hub_setup_prb(nasid, prbnum, credits, conveyor)
818c2ecf20Sopenharmony_ci *
828c2ecf20Sopenharmony_ci *	Put a PRB into fire-and-forget mode if conveyor isn't set.  Otherwise,
838c2ecf20Sopenharmony_ci *	put it into conveyor belt mode with the specified number of credits.
848c2ecf20Sopenharmony_ci */
858c2ecf20Sopenharmony_cistatic void hub_setup_prb(nasid_t nasid, int prbnum, int credits)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	union iprb_u prb;
888c2ecf20Sopenharmony_ci	int prb_offset;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	/*
918c2ecf20Sopenharmony_ci	 * Get the current register value.
928c2ecf20Sopenharmony_ci	 */
938c2ecf20Sopenharmony_ci	prb_offset = IIO_IOPRB(prbnum);
948c2ecf20Sopenharmony_ci	prb.iprb_regval = REMOTE_HUB_L(nasid, prb_offset);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	/*
978c2ecf20Sopenharmony_ci	 * Clear out some fields.
988c2ecf20Sopenharmony_ci	 */
998c2ecf20Sopenharmony_ci	prb.iprb_ovflow = 1;
1008c2ecf20Sopenharmony_ci	prb.iprb_bnakctr = 0;
1018c2ecf20Sopenharmony_ci	prb.iprb_anakctr = 0;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	/*
1048c2ecf20Sopenharmony_ci	 * Enable or disable fire-and-forget mode.
1058c2ecf20Sopenharmony_ci	 */
1068c2ecf20Sopenharmony_ci	prb.iprb_ff = force_fire_and_forget ? 1 : 0;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	/*
1098c2ecf20Sopenharmony_ci	 * Set the appropriate number of PIO credits for the widget.
1108c2ecf20Sopenharmony_ci	 */
1118c2ecf20Sopenharmony_ci	prb.iprb_xtalkctr = credits;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	/*
1148c2ecf20Sopenharmony_ci	 * Store the new value to the register.
1158c2ecf20Sopenharmony_ci	 */
1168c2ecf20Sopenharmony_ci	REMOTE_HUB_S(nasid, prb_offset, prb.iprb_regval);
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci/**
1208c2ecf20Sopenharmony_ci * hub_set_piomode  -  set pio mode for a given hub
1218c2ecf20Sopenharmony_ci *
1228c2ecf20Sopenharmony_ci * @nasid:	physical node ID for the hub in question
1238c2ecf20Sopenharmony_ci *
1248c2ecf20Sopenharmony_ci * Put the hub into either "PIO conveyor belt" mode or "fire-and-forget" mode.
1258c2ecf20Sopenharmony_ci * To do this, we have to make absolutely sure that no PIOs are in progress
1268c2ecf20Sopenharmony_ci * so we turn off access to all widgets for the duration of the function.
1278c2ecf20Sopenharmony_ci *
1288c2ecf20Sopenharmony_ci * XXX - This code should really check what kind of widget we're talking
1298c2ecf20Sopenharmony_ci * to.	Bridges can only handle three requests, but XG will do more.
1308c2ecf20Sopenharmony_ci * How many can crossbow handle to widget 0?  We're assuming 1.
1318c2ecf20Sopenharmony_ci *
1328c2ecf20Sopenharmony_ci * XXX - There is a bug in the crossbow that link reset PIOs do not
1338c2ecf20Sopenharmony_ci * return write responses.  The easiest solution to this problem is to
1348c2ecf20Sopenharmony_ci * leave widget 0 (xbow) in fire-and-forget mode at all times.	This
1358c2ecf20Sopenharmony_ci * only affects pio's to xbow registers, which should be rare.
1368c2ecf20Sopenharmony_ci **/
1378c2ecf20Sopenharmony_cistatic void hub_set_piomode(nasid_t nasid)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	u64 ii_iowa;
1408c2ecf20Sopenharmony_ci	union hubii_wcr_u ii_wcr;
1418c2ecf20Sopenharmony_ci	unsigned i;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	ii_iowa = REMOTE_HUB_L(nasid, IIO_OUTWIDGET_ACCESS);
1448c2ecf20Sopenharmony_ci	REMOTE_HUB_S(nasid, IIO_OUTWIDGET_ACCESS, 0);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	ii_wcr.wcr_reg_value = REMOTE_HUB_L(nasid, IIO_WCR);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	if (ii_wcr.iwcr_dir_con) {
1498c2ecf20Sopenharmony_ci		/*
1508c2ecf20Sopenharmony_ci		 * Assume a bridge here.
1518c2ecf20Sopenharmony_ci		 */
1528c2ecf20Sopenharmony_ci		hub_setup_prb(nasid, 0, 3);
1538c2ecf20Sopenharmony_ci	} else {
1548c2ecf20Sopenharmony_ci		/*
1558c2ecf20Sopenharmony_ci		 * Assume a crossbow here.
1568c2ecf20Sopenharmony_ci		 */
1578c2ecf20Sopenharmony_ci		hub_setup_prb(nasid, 0, 1);
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	/*
1618c2ecf20Sopenharmony_ci	 * XXX - Here's where we should take the widget type into
1628c2ecf20Sopenharmony_ci	 * when account assigning credits.
1638c2ecf20Sopenharmony_ci	 */
1648c2ecf20Sopenharmony_ci	for (i = HUB_WIDGET_ID_MIN; i <= HUB_WIDGET_ID_MAX; i++)
1658c2ecf20Sopenharmony_ci		hub_setup_prb(nasid, i, 3);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	REMOTE_HUB_S(nasid, IIO_OUTWIDGET_ACCESS, ii_iowa);
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci/*
1718c2ecf20Sopenharmony_ci * hub_pio_init	 -  PIO-related hub initialization
1728c2ecf20Sopenharmony_ci *
1738c2ecf20Sopenharmony_ci * @hub:	hubinfo structure for our hub
1748c2ecf20Sopenharmony_ci */
1758c2ecf20Sopenharmony_civoid hub_pio_init(nasid_t nasid)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	unsigned i;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	/* initialize big window piomaps for this hub */
1808c2ecf20Sopenharmony_ci	bitmap_zero(hub_data(nasid)->h_bigwin_used, HUB_NUM_BIG_WINDOW);
1818c2ecf20Sopenharmony_ci	for (i = 0; i < HUB_NUM_BIG_WINDOW; i++)
1828c2ecf20Sopenharmony_ci		IIO_ITTE_DISABLE(nasid, i);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	hub_set_piomode(nasid);
1858c2ecf20Sopenharmony_ci}
186