162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
362306a36Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
462306a36Sopenharmony_ci * for more details.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * SGI UV Core Functions
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Copyright (C) 2008 Silicon Graphics, Inc. All rights reserved.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/acpi.h>
1262306a36Sopenharmony_ci#include <linux/efi.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/percpu.h>
1562306a36Sopenharmony_ci#include <asm/uv/uv.h>
1662306a36Sopenharmony_ci#include <asm/uv/uv_mmrs.h>
1762306a36Sopenharmony_ci#include <asm/uv/uv_hub.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cibool ia64_is_uv;
2062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ia64_is_uv);
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ciDEFINE_PER_CPU(struct uv_hub_info_s, __uv_hub_info);
2362306a36Sopenharmony_ciEXPORT_PER_CPU_SYMBOL_GPL(__uv_hub_info);
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistruct redir_addr {
2662306a36Sopenharmony_ci	unsigned long redirect;
2762306a36Sopenharmony_ci	unsigned long alias;
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define DEST_SHIFT UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_0_MMR_DEST_BASE_SHFT
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic __initdata struct redir_addr redir_addrs[] = {
3362306a36Sopenharmony_ci	{UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_0_MMR, UVH_SI_ALIAS0_OVERLAY_CONFIG},
3462306a36Sopenharmony_ci	{UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_1_MMR, UVH_SI_ALIAS1_OVERLAY_CONFIG},
3562306a36Sopenharmony_ci	{UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_2_MMR, UVH_SI_ALIAS2_OVERLAY_CONFIG},
3662306a36Sopenharmony_ci};
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic __init void get_lowmem_redirect(unsigned long *base, unsigned long *size)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	union uvh_si_alias0_overlay_config_u alias;
4162306a36Sopenharmony_ci	union uvh_rh_gam_alias210_redirect_config_2_mmr_u redirect;
4262306a36Sopenharmony_ci	int i;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(redir_addrs); i++) {
4562306a36Sopenharmony_ci		alias.v = uv_read_local_mmr(redir_addrs[i].alias);
4662306a36Sopenharmony_ci		if (alias.s.base == 0) {
4762306a36Sopenharmony_ci			*size = (1UL << alias.s.m_alias);
4862306a36Sopenharmony_ci			redirect.v = uv_read_local_mmr(redir_addrs[i].redirect);
4962306a36Sopenharmony_ci			*base = (unsigned long)redirect.s.dest_base << DEST_SHIFT;
5062306a36Sopenharmony_ci			return;
5162306a36Sopenharmony_ci		}
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci	BUG();
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_civoid __init uv_probe_system_type(void)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	struct acpi_table_rsdp *rsdp;
5962306a36Sopenharmony_ci	struct acpi_table_xsdt *xsdt;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	if (efi.acpi20 == EFI_INVALID_TABLE_ADDR) {
6262306a36Sopenharmony_ci		pr_err("ACPI 2.0 RSDP not found.\n");
6362306a36Sopenharmony_ci		return;
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	rsdp = (struct acpi_table_rsdp *)__va(efi.acpi20);
6762306a36Sopenharmony_ci	if (strncmp(rsdp->signature, ACPI_SIG_RSDP, sizeof(ACPI_SIG_RSDP) - 1)) {
6862306a36Sopenharmony_ci		pr_err("ACPI 2.0 RSDP signature incorrect.\n");
6962306a36Sopenharmony_ci		return;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	xsdt = (struct acpi_table_xsdt *)__va(rsdp->xsdt_physical_address);
7362306a36Sopenharmony_ci	if (strncmp(xsdt->header.signature, ACPI_SIG_XSDT,
7462306a36Sopenharmony_ci			sizeof(ACPI_SIG_XSDT) - 1)) {
7562306a36Sopenharmony_ci		pr_err("ACPI 2.0 XSDT signature incorrect.\n");
7662306a36Sopenharmony_ci		return;
7762306a36Sopenharmony_ci	}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (!strcmp(xsdt->header.oem_id, "SGI") &&
8062306a36Sopenharmony_ci	    !strcmp(xsdt->header.oem_table_id + 4, "UV"))
8162306a36Sopenharmony_ci		ia64_is_uv = true;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_civoid __init uv_setup(char **cmdline_p)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	union uvh_si_addr_map_config_u m_n_config;
8762306a36Sopenharmony_ci	union uvh_node_id_u node_id;
8862306a36Sopenharmony_ci	unsigned long gnode_upper;
8962306a36Sopenharmony_ci	int nid, cpu, m_val, n_val;
9062306a36Sopenharmony_ci	unsigned long mmr_base, lowmem_redir_base, lowmem_redir_size;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	get_lowmem_redirect(&lowmem_redir_base, &lowmem_redir_size);
9362306a36Sopenharmony_ci	node_id.v = uv_read_local_mmr(UVH_NODE_ID);
9462306a36Sopenharmony_ci	m_n_config.v = uv_read_local_mmr(UVH_SI_ADDR_MAP_CONFIG);
9562306a36Sopenharmony_ci	mmr_base = uv_read_local_mmr(UVH_RH_GAM_MMR_OVERLAY_CONFIG_MMR) &
9662306a36Sopenharmony_ci			~UV_MMR_ENABLE;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	m_val = m_n_config.s.m_skt;
9962306a36Sopenharmony_ci	n_val = m_n_config.s.n_skt;
10062306a36Sopenharmony_ci	printk(KERN_DEBUG "UV: global MMR base 0x%lx\n", mmr_base);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	gnode_upper = (((unsigned long)node_id.s.node_id) &
10362306a36Sopenharmony_ci		       ~((1 << n_val) - 1)) << m_val;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	for_each_present_cpu(cpu) {
10662306a36Sopenharmony_ci		nid = cpu_to_node(cpu);
10762306a36Sopenharmony_ci		uv_cpu_hub_info(cpu)->lowmem_remap_base = lowmem_redir_base;
10862306a36Sopenharmony_ci		uv_cpu_hub_info(cpu)->lowmem_remap_top =
10962306a36Sopenharmony_ci			lowmem_redir_base + lowmem_redir_size;
11062306a36Sopenharmony_ci		uv_cpu_hub_info(cpu)->m_val = m_val;
11162306a36Sopenharmony_ci		uv_cpu_hub_info(cpu)->n_val = n_val;
11262306a36Sopenharmony_ci		uv_cpu_hub_info(cpu)->pnode_mask = (1 << n_val) -1;
11362306a36Sopenharmony_ci		uv_cpu_hub_info(cpu)->gpa_mask = (1 << (m_val + n_val)) - 1;
11462306a36Sopenharmony_ci		uv_cpu_hub_info(cpu)->gnode_upper = gnode_upper;
11562306a36Sopenharmony_ci		uv_cpu_hub_info(cpu)->global_mmr_base = mmr_base;
11662306a36Sopenharmony_ci		uv_cpu_hub_info(cpu)->coherency_domain_number = 0;/* ZZZ */
11762306a36Sopenharmony_ci		printk(KERN_DEBUG "UV cpu %d, nid %d\n", cpu, nid);
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
121