Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2a919596 authored by Jack Steiner's avatar Jack Steiner Committed by Ingo Molnar
Browse files

x86, UV: Add support for SGI UV2 hub chip



This patch adds support for a new version of the SGI UV hub
chip. The hub chip is the node controller that connects multiple
blades into a larger coherent SSI.

For the most part, UV2 is compatible with UV1. The majority of
the changes are in the addresses of MMRs and in a few cases, the
contents of MMRs. These changes are the result in changes in the
system topology such as node configuration, processor types,
maximum nodes, physical address sizes, etc.

Signed-off-by: default avatarJack Steiner <steiner@sgi.com>
Link: http://lkml.kernel.org/r/20110511175028.GA18006@sgi.com


Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent 7ccafc5f
Loading
Loading
Loading
Loading
+38 −4
Original line number Diff line number Diff line
@@ -44,7 +44,10 @@
#define UV_ACT_STATUS_SIZE		2
#define UV_DISTRIBUTION_SIZE		256
#define UV_SW_ACK_NPENDING		8
#define UV_NET_ENDPOINT_INTD		0x38
#define UV1_NET_ENDPOINT_INTD		0x38
#define UV2_NET_ENDPOINT_INTD		0x28
#define UV_NET_ENDPOINT_INTD		(is_uv1_hub() ?			\
			UV1_NET_ENDPOINT_INTD : UV2_NET_ENDPOINT_INTD)
#define UV_DESC_BASE_PNODE_SHIFT	49
#define UV_PAYLOADQ_PNODE_SHIFT		49
#define UV_PTC_BASENAME			"sgi_uv/ptc_statistics"
@@ -53,10 +56,22 @@
#define UV_BAU_TUNABLES_FILE		"bau_tunables"
#define WHITESPACE			" \t\n"
#define uv_physnodeaddr(x)		((__pa((unsigned long)(x)) & uv_mmask))
#define UV_ENABLE_INTD_SOFT_ACK_MODE_SHIFT 15
#define UV_INTD_SOFT_ACK_TIMEOUT_PERIOD_SHIFT 16
#define UV_INTD_SOFT_ACK_TIMEOUT_PERIOD 0x0000000009UL


/* [19:16] SOFT_ACK timeout period  19: 1 is urgency 7  17:16 1 is multiplier */
/*
 * UV2: Bit 19 selects between
 *  (0): 10 microsecond timebase and
 *  (1): 80 microseconds
 *  we're using 655us, similar to UV1: 65 units of 10us
 */
#define UV1_INTD_SOFT_ACK_TIMEOUT_PERIOD (9UL)
#define UV2_INTD_SOFT_ACK_TIMEOUT_PERIOD (65*10UL)

#define UV_INTD_SOFT_ACK_TIMEOUT_PERIOD	(is_uv1_hub() ?			\
		UV1_INTD_SOFT_ACK_TIMEOUT_PERIOD :			\
		UV2_INTD_SOFT_ACK_TIMEOUT_PERIOD)

#define BAU_MISC_CONTROL_MULT_MASK 3

#define UVH_AGING_PRESCALE_SEL 0x000000b000UL
@@ -76,6 +91,16 @@
#define DESC_STATUS_ACTIVE		1
#define DESC_STATUS_DESTINATION_TIMEOUT	2
#define DESC_STATUS_SOURCE_TIMEOUT	3
/*
 * bits put together from HRP_LB_BAU_SB_ACTIVATION_STATUS_0/1/2
 * values 1 and 5 will not occur
 */
#define UV2H_DESC_IDLE			0
#define UV2H_DESC_DEST_TIMEOUT		2
#define UV2H_DESC_DEST_STRONG_NACK	3
#define UV2H_DESC_BUSY			4
#define UV2H_DESC_SOURCE_TIMEOUT	6
#define UV2H_DESC_DEST_PUT_ERR		7

/*
 * delay for 'plugged' timeout retries, in microseconds
@@ -96,6 +121,15 @@

#define UV_LB_SUBNODEID 0x10

/* these two are the same for UV1 and UV2: */
#define UV_SA_SHFT UVH_LB_BAU_MISC_CONTROL_INTD_SOFT_ACK_TIMEOUT_PERIOD_SHFT
#define UV_SA_MASK UVH_LB_BAU_MISC_CONTROL_INTD_SOFT_ACK_TIMEOUT_PERIOD_MASK
/* 4 bits of software ack period */
#define UV2_ACK_MASK 0x7UL
#define UV2_ACK_UNITS_SHFT 3
#define UV2_LEG_SHFT UV2H_LB_BAU_MISC_CONTROL_USE_LEGACY_DESCRIPTOR_FORMATS_SHFT
#define UV2_EXT_SHFT UV2H_LB_BAU_MISC_CONTROL_ENABLE_EXTENDED_SB_STATUS_SHFT

/*
 * number of entries in the destination side payload queue
 */
+58 −13
Original line number Diff line number Diff line
@@ -77,8 +77,9 @@
 *
 *		1111110000000000
 *		5432109876543210
 *		pppppppppplc0cch	Nehalem-EX
 *		ppppppppplcc0cch	Westmere-EX
 *		pppppppppplc0cch	Nehalem-EX (12 bits in hdw reg)
 *		ppppppppplcc0cch	Westmere-EX (12 bits in hdw reg)
 *		pppppppppppcccch	SandyBridge (15 bits in hdw reg)
 *		sssssssssss
 *
 *			p  = pnode bits
@@ -87,7 +88,7 @@
 *			h  = hyperthread
 *			s  = bits that are in the SOCKET_ID CSR
 *
 *	Note: Processor only supports 12 bits in the APICID register. The ACPI
 *	Note: Processor may support fewer bits in the APICID register. The ACPI
 *	      tables hold all 16 bits. Software needs to be aware of this.
 *
 *	      Unless otherwise specified, all references to APICID refer to
@@ -138,6 +139,8 @@ struct uv_hub_info_s {
	unsigned long		global_mmr_base;
	unsigned long		gpa_mask;
	unsigned int		gnode_extra;
	unsigned char		hub_revision;
	unsigned char		apic_pnode_shift;
	unsigned long		gnode_upper;
	unsigned long		lowmem_remap_top;
	unsigned long		lowmem_remap_base;
@@ -149,13 +152,31 @@ struct uv_hub_info_s {
	unsigned char		m_val;
	unsigned char		n_val;
	struct uv_scir_s	scir;
	unsigned char		apic_pnode_shift;
};

DECLARE_PER_CPU(struct uv_hub_info_s, __uv_hub_info);
#define uv_hub_info		(&__get_cpu_var(__uv_hub_info))
#define uv_cpu_hub_info(cpu)	(&per_cpu(__uv_hub_info, cpu))

/*
 * Hub revisions less than UV2_HUB_REVISION_BASE are UV1 hubs. All UV2
 * hubs have revision numbers greater than or equal to UV2_HUB_REVISION_BASE.
 * This is a software convention - NOT the hardware revision numbers in
 * the hub chip.
 */
#define UV1_HUB_REVISION_BASE		1
#define UV2_HUB_REVISION_BASE		3

static inline int is_uv1_hub(void)
{
	return uv_hub_info->hub_revision < UV2_HUB_REVISION_BASE;
}

static inline int is_uv2_hub(void)
{
	return uv_hub_info->hub_revision >= UV2_HUB_REVISION_BASE;
}

union uvh_apicid {
    unsigned long       v;
    struct uvh_apicid_s {
@@ -180,11 +201,25 @@ union uvh_apicid {
#define UV_PNODE_TO_GNODE(p)		((p) |uv_hub_info->gnode_extra)
#define UV_PNODE_TO_NASID(p)		(UV_PNODE_TO_GNODE(p) << 1)

#define UV_LOCAL_MMR_BASE		0xf4000000UL
#define UV_GLOBAL_MMR32_BASE		0xf8000000UL
#define UV1_LOCAL_MMR_BASE		0xf4000000UL
#define UV1_GLOBAL_MMR32_BASE		0xf8000000UL
#define UV1_LOCAL_MMR_SIZE		(64UL * 1024 * 1024)
#define UV1_GLOBAL_MMR32_SIZE		(64UL * 1024 * 1024)

#define UV2_LOCAL_MMR_BASE		0xfa000000UL
#define UV2_GLOBAL_MMR32_BASE		0xfc000000UL
#define UV2_LOCAL_MMR_SIZE		(32UL * 1024 * 1024)
#define UV2_GLOBAL_MMR32_SIZE		(32UL * 1024 * 1024)

#define UV_LOCAL_MMR_BASE		(is_uv1_hub() ? UV1_LOCAL_MMR_BASE     \
						: UV2_LOCAL_MMR_BASE)
#define UV_GLOBAL_MMR32_BASE		(is_uv1_hub() ? UV1_GLOBAL_MMR32_BASE  \
						: UV2_GLOBAL_MMR32_BASE)
#define UV_LOCAL_MMR_SIZE		(is_uv1_hub() ? UV1_LOCAL_MMR_SIZE :   \
						UV2_LOCAL_MMR_SIZE)
#define UV_GLOBAL_MMR32_SIZE		(is_uv1_hub() ? UV1_GLOBAL_MMR32_SIZE :\
						UV2_GLOBAL_MMR32_SIZE)
#define UV_GLOBAL_MMR64_BASE		(uv_hub_info->global_mmr_base)
#define UV_LOCAL_MMR_SIZE		(64UL * 1024 * 1024)
#define UV_GLOBAL_MMR32_SIZE		(64UL * 1024 * 1024)

#define UV_GLOBAL_GRU_MMR_BASE		0x4000000

@@ -300,6 +335,17 @@ static inline int uv_apicid_to_pnode(int apicid)
	return (apicid >> uv_hub_info->apic_pnode_shift);
}

/*
 * Convert an apicid to the socket number on the blade
 */
static inline int uv_apicid_to_socket(int apicid)
{
	if (is_uv1_hub())
		return (apicid >> (uv_hub_info->apic_pnode_shift - 1)) & 1;
	else
		return 0;
}

/*
 * Access global MMRs using the low memory MMR32 space. This region supports
 * faster MMR access but not all MMRs are accessible in this space.
@@ -519,14 +565,13 @@ static inline void uv_hub_send_ipi(int pnode, int apicid, int vector)

/*
 * Get the minimum revision number of the hub chips within the partition.
 *     1 - initial rev 1.0 silicon
 *     2 - rev 2.0 production silicon
 *     1 - UV1 rev 1.0 initial silicon
 *     2 - UV1 rev 2.0 production silicon
 *     3 - UV2 rev 1.0 initial silicon
 */
static inline int uv_get_min_hub_revision_id(void)
{
	extern int uv_min_hub_revision_id;

	return uv_min_hub_revision_id;
	return uv_hub_info->hub_revision;
}

#endif /* CONFIG_X86_64 */
+822 −190

File changed.

Preview size limit exceeded, changes collapsed.

+31 −9
Original line number Diff line number Diff line
@@ -91,6 +91,10 @@ static int __init early_get_pnodeid(void)
	m_n_config.v = uv_early_read_mmr(UVH_RH_GAM_CONFIG_MMR);
	uv_min_hub_revision_id = node_id.s.revision;

	if (node_id.s.part_number == UV2_HUB_PART_NUMBER)
		uv_min_hub_revision_id += UV2_HUB_REVISION_BASE - 1;

	uv_hub_info->hub_revision = uv_min_hub_revision_id;
	pnode = (node_id.s.node_id >> 1) & ((1 << m_n_config.s.n_skt) - 1);
	return pnode;
}
@@ -112,17 +116,25 @@ static void __init early_get_apic_pnode_shift(void)
 */
static void __init uv_set_apicid_hibit(void)
{
	union uvh_lb_target_physical_apic_id_mask_u apicid_mask;
	union uv1h_lb_target_physical_apic_id_mask_u apicid_mask;

	apicid_mask.v = uv_early_read_mmr(UVH_LB_TARGET_PHYSICAL_APIC_ID_MASK);
	uv_apicid_hibits = apicid_mask.s.bit_enables & UV_APICID_HIBIT_MASK;
	if (is_uv1_hub()) {
		apicid_mask.v =
			uv_early_read_mmr(UV1H_LB_TARGET_PHYSICAL_APIC_ID_MASK);
		uv_apicid_hibits =
			apicid_mask.s1.bit_enables & UV_APICID_HIBIT_MASK;
	}
}

static int __init uv_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
{
	int pnodeid;
	int pnodeid, is_uv1, is_uv2;

	if (!strcmp(oem_id, "SGI")) {
	is_uv1 = !strcmp(oem_id, "SGI");
	is_uv2 = !strcmp(oem_id, "SGI2");
	if (is_uv1 || is_uv2) {
		uv_hub_info->hub_revision =
			is_uv1 ? UV1_HUB_REVISION_BASE : UV2_HUB_REVISION_BASE;
		pnodeid = early_get_pnodeid();
		early_get_apic_pnode_shift();
		x86_platform.is_untracked_pat_range =  uv_is_untracked_pat_range;
@@ -484,13 +496,20 @@ static __init void map_mmr_high(int max_pnode)
static __init void map_mmioh_high(int max_pnode)
{
	union uvh_rh_gam_mmioh_overlay_config_mmr_u mmioh;
	int shift = UVH_RH_GAM_MMIOH_OVERLAY_CONFIG_MMR_BASE_SHFT;
	int shift;

	mmioh.v = uv_read_local_mmr(UVH_RH_GAM_MMIOH_OVERLAY_CONFIG_MMR);
	if (mmioh.s.enable)
		map_high("MMIOH", mmioh.s.base, shift, mmioh.s.m_io,
	if (is_uv1_hub() && mmioh.s1.enable) {
		shift = UV1H_RH_GAM_MMIOH_OVERLAY_CONFIG_MMR_BASE_SHFT;
		map_high("MMIOH", mmioh.s1.base, shift, mmioh.s1.m_io,
			max_pnode, map_uc);
	}
	if (is_uv2_hub() && mmioh.s2.enable) {
		shift = UV2H_RH_GAM_MMIOH_OVERLAY_CONFIG_MMR_BASE_SHFT;
		map_high("MMIOH", mmioh.s2.base, shift, mmioh.s2.m_io,
			max_pnode, map_uc);
	}
}

static __init void map_low_mmrs(void)
{
@@ -736,13 +755,14 @@ void __init uv_system_init(void)
	unsigned long mmr_base, present, paddr;
	unsigned short pnode_mask, pnode_io_mask;

	printk(KERN_INFO "UV: Found %s hub\n", is_uv1_hub() ? "UV1" : "UV2");
	map_low_mmrs();

	m_n_config.v = uv_read_local_mmr(UVH_RH_GAM_CONFIG_MMR );
	m_val = m_n_config.s.m_skt;
	n_val = m_n_config.s.n_skt;
	mmioh.v = uv_read_local_mmr(UVH_RH_GAM_MMIOH_OVERLAY_CONFIG_MMR);
	n_io = mmioh.s.n_io;
	n_io = is_uv1_hub() ? mmioh.s1.n_io : mmioh.s2.n_io;
	mmr_base =
	    uv_read_local_mmr(UVH_RH_GAM_MMR_OVERLAY_CONFIG_MMR) &
	    ~UV_MMR_ENABLE;
@@ -811,6 +831,8 @@ void __init uv_system_init(void)
		 */
		uv_cpu_hub_info(cpu)->pnode_mask = pnode_mask;
		uv_cpu_hub_info(cpu)->apic_pnode_shift = uvh_apicid.s.pnode_shift;
		uv_cpu_hub_info(cpu)->hub_revision = uv_hub_info->hub_revision;

		pnode = uv_apicid_to_pnode(apicid);
		blade = boot_pnode_to_blade(pnode);
		lcpu = uv_blade_info[blade].nr_possible_cpus;
+114 −18
Original line number Diff line number Diff line
@@ -397,16 +397,13 @@ end_uvhub_quiesce(struct bau_control *hmaster)
 * Wait for completion of a broadcast software ack message
 * return COMPLETE, RETRY(PLUGGED or TIMEOUT) or GIVEUP
 */
static int uv_wait_completion(struct bau_desc *bau_desc,
static int uv1_wait_completion(struct bau_desc *bau_desc,
	unsigned long mmr_offset, int right_shift, int this_cpu,
	struct bau_control *bcp, struct bau_control *smaster, long try)
{
	unsigned long descriptor_status;
	cycles_t ttime;
	struct ptc_stats *stat = bcp->statp;
	struct bau_control *hmaster;

	hmaster = bcp->uvhub_master;

	/* spin on the status MMR, waiting for it to go idle */
	while ((descriptor_status = (((unsigned long)
@@ -414,10 +411,10 @@ static int uv_wait_completion(struct bau_desc *bau_desc,
			right_shift) & UV_ACT_STATUS_MASK)) !=
			DESC_STATUS_IDLE) {
		/*
		 * Our software ack messages may be blocked because there are
		 * no swack resources available.  As long as none of them
		 * has timed out hardware will NACK our message and its
		 * state will stay IDLE.
		 * Our software ack messages may be blocked because
		 * there are no swack resources available.  As long
		 * as none of them has timed out hardware will NACK
		 * our message and its state will stay IDLE.
		 */
		if (descriptor_status == DESC_STATUS_SOURCE_TIMEOUT) {
			stat->s_stimeout++;
@@ -452,6 +449,86 @@ static int uv_wait_completion(struct bau_desc *bau_desc,
	return FLUSH_COMPLETE;
}

static int uv2_wait_completion(struct bau_desc *bau_desc,
	unsigned long mmr_offset, int right_shift, int this_cpu,
	struct bau_control *bcp, struct bau_control *smaster, long try)
{
	unsigned long descriptor_status;
	unsigned long descriptor_status2;
	int cpu;
	cycles_t ttime;
	struct ptc_stats *stat = bcp->statp;

	/* UV2 has an extra bit of status */
	cpu = bcp->uvhub_cpu;
	/* spin on the status MMR, waiting for it to go idle */
	descriptor_status = (((unsigned long)(uv_read_local_mmr
		(mmr_offset)) >> right_shift) & UV_ACT_STATUS_MASK);
	descriptor_status2 = (((unsigned long)uv_read_local_mmr
		(UV2H_LB_BAU_SB_ACTIVATION_STATUS_2) >> cpu) & 0x1UL);
	descriptor_status = (descriptor_status << 1) |
		descriptor_status2;
	while (descriptor_status != UV2H_DESC_IDLE) {
		/*
		 * Our software ack messages may be blocked because
		 * there are no swack resources available.  As long
		 * as none of them has timed out hardware will NACK
		 * our message and its state will stay IDLE.
		 */
		if ((descriptor_status == UV2H_DESC_SOURCE_TIMEOUT) ||
		    (descriptor_status == UV2H_DESC_DEST_STRONG_NACK) ||
		    (descriptor_status == UV2H_DESC_DEST_PUT_ERR)) {
			stat->s_stimeout++;
			return FLUSH_GIVEUP;
		} else if (descriptor_status == UV2H_DESC_DEST_TIMEOUT) {
			stat->s_dtimeout++;
			ttime = get_cycles();

			/*
			 * Our retries may be blocked by all destination
			 * swack resources being consumed, and a timeout
			 * pending.  In that case hardware returns the
			 * ERROR that looks like a destination timeout.
			 */
			if (cycles_2_us(ttime - bcp->send_message) <
							timeout_us) {
				bcp->conseccompletes = 0;
				return FLUSH_RETRY_PLUGGED;
			}

			bcp->conseccompletes = 0;
			return FLUSH_RETRY_TIMEOUT;
		} else {
			/*
			 * descriptor_status is still BUSY
			 */
			cpu_relax();
		}
		descriptor_status = (((unsigned long)(uv_read_local_mmr
			(mmr_offset)) >> right_shift) &
			UV_ACT_STATUS_MASK);
		descriptor_status2 = (((unsigned long)uv_read_local_mmr
			(UV2H_LB_BAU_SB_ACTIVATION_STATUS_2) >> cpu) &
			0x1UL);
		descriptor_status = (descriptor_status << 1) |
			descriptor_status2;
	}
	bcp->conseccompletes++;
	return FLUSH_COMPLETE;
}

static int uv_wait_completion(struct bau_desc *bau_desc,
	unsigned long mmr_offset, int right_shift, int this_cpu,
	struct bau_control *bcp, struct bau_control *smaster, long try)
{
	if (is_uv1_hub())
		return uv1_wait_completion(bau_desc, mmr_offset, right_shift,
				   this_cpu, bcp, smaster, try);
	else
		return uv2_wait_completion(bau_desc, mmr_offset, right_shift,
				   this_cpu, bcp, smaster, try);
}

static inline cycles_t
sec_2_cycles(unsigned long sec)
{
@@ -585,7 +662,8 @@ int uv_flush_send_and_wait(struct bau_desc *bau_desc,
	struct bau_control *smaster = bcp->socket_master;
	struct bau_control *hmaster = bcp->uvhub_master;

	if (!atomic_inc_unless_ge(&hmaster->uvhub_lock,
	if (is_uv1_hub()  &&
			!atomic_inc_unless_ge(&hmaster->uvhub_lock,
			&hmaster->active_descriptor_count,
			hmaster->max_bau_concurrent)) {
		stat->s_throttles++;
@@ -899,12 +977,17 @@ static void __init uv_enable_timeouts(void)
		uv_write_global_mmr64
		    (pnode, UVH_LB_BAU_MISC_CONTROL, mmr_image);
		/*
		 * UV1:
		 * Subsequent reversals of the timebase bit (3) cause an
		 * immediate timeout of one or all INTD resources as
		 * indicated in bits 2:0 (7 causes all of them to timeout).
		 */
		mmr_image |= ((unsigned long)1 <<
		    UVH_LB_BAU_MISC_CONTROL_ENABLE_INTD_SOFT_ACK_MODE_SHFT);
		if (is_uv2_hub()) {
			mmr_image |= ((unsigned long)1 << UV2_LEG_SHFT);
			mmr_image |= ((unsigned long)1 << UV2_EXT_SHFT);
		}
		uv_write_global_mmr64
		    (pnode, UVH_LB_BAU_MISC_CONTROL, mmr_image);
	}
@@ -1486,7 +1569,9 @@ calculate_destination_timeout(void)
	int ret;
	unsigned long ts_ns;

	mult1 = UV_INTD_SOFT_ACK_TIMEOUT_PERIOD & BAU_MISC_CONTROL_MULT_MASK;
	if (is_uv1_hub()) {
		mult1 = UV1_INTD_SOFT_ACK_TIMEOUT_PERIOD &
			BAU_MISC_CONTROL_MULT_MASK;
		mmr_image = uv_read_local_mmr(UVH_AGING_PRESCALE_SEL);
		index = (mmr_image >> BAU_URGENCY_7_SHIFT) & BAU_URGENCY_7_MASK;
		mmr_image = uv_read_local_mmr(UVH_TRANSACTION_TIMEOUT);
@@ -1494,6 +1579,17 @@ calculate_destination_timeout(void)
		base = timeout_base_ns[index];
		ts_ns = base * mult1 * mult2;
		ret = ts_ns / 1000;
	} else {
		/* 4 bits  0/1 for 10/80us, 3 bits of multiplier */
		mmr_image = uv_read_local_mmr(UVH_AGING_PRESCALE_SEL);
		mmr_image = (mmr_image & UV_SA_MASK) >> UV_SA_SHFT;
		if (mmr_image & ((unsigned long)1 << UV2_ACK_UNITS_SHFT))
			mult1 = 80;
		else
			mult1 = 10;
		base = mmr_image & UV2_ACK_MASK;
		ret = mult1 * base;
	}
	return ret;
}

Loading