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