Commit 8b6591fd authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'x86_platform_for_v5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull x86 platform updates from Borislav Petkov:

 - Cleanup different aspects of the UV code and start adding support for
   the new UV5 class of systems (Mike Travis)

 - Use a flexible array for a dynamically sized struct uv_rtc_timer_head
   (Gustavo A. R. Silva)

* tag 'x86_platform_for_v5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86/platform/uv: Update Copyrights to conform to HPE standards
  x86/platform/uv: Update for UV5 NMI MMR changes
  x86/platform/uv: Update UV5 TSC checking
  x86/platform/uv: Update node present counting
  x86/platform/uv: Update UV5 MMR references in UV GRU
  x86/platform/uv: Adjust GAM MMR references affected by UV5 updates
  x86/platform/uv: Update MMIOH references based on new UV5 MMRs
  x86/platform/uv: Add and decode Arch Type in UVsystab
  x86/platform/uv: Add UV5 direct references
  x86/platform/uv: Update UV MMRs for UV5
  drivers/misc/sgi-xp: Adjust references in UV kernel modules
  x86/platform/uv: Remove SCIR MMR references for UV systems
  x86/platform/uv: Remove UV BAU TLB Shootdown Handler
  x86/uv/time: Use a flexible array in struct uv_rtc_timer_head
parents 92a0610b 7a6d94f0
...@@ -591,10 +591,6 @@ DECLARE_IDTENTRY_SYSVEC(CALL_FUNCTION_VECTOR, sysvec_call_function); ...@@ -591,10 +591,6 @@ DECLARE_IDTENTRY_SYSVEC(CALL_FUNCTION_VECTOR, sysvec_call_function);
#endif #endif
#ifdef CONFIG_X86_LOCAL_APIC #ifdef CONFIG_X86_LOCAL_APIC
# ifdef CONFIG_X86_UV
DECLARE_IDTENTRY_SYSVEC(UV_BAU_MESSAGE, sysvec_uv_bau_message);
# endif
# ifdef CONFIG_X86_MCE_THRESHOLD # ifdef CONFIG_X86_MCE_THRESHOLD
DECLARE_IDTENTRY_SYSVEC(THRESHOLD_APIC_VECTOR, sysvec_threshold); DECLARE_IDTENTRY_SYSVEC(THRESHOLD_APIC_VECTOR, sysvec_threshold);
# endif # endif
......
...@@ -5,8 +5,9 @@ ...@@ -5,8 +5,9 @@
/* /*
* UV BIOS layer definitions. * UV BIOS layer definitions.
* *
* Copyright (c) 2008-2009 Silicon Graphics, Inc. All Rights Reserved. * (C) Copyright 2020 Hewlett Packard Enterprise Development LP
* Copyright (c) Russ Anderson <rja@sgi.com> * Copyright (C) 2007-2017 Silicon Graphics, Inc. All rights reserved.
* Copyright (c) Russ Anderson <rja@sgi.com>
*/ */
#include <linux/rtc.h> #include <linux/rtc.h>
...@@ -71,6 +72,11 @@ struct uv_gam_range_entry { ...@@ -71,6 +72,11 @@ struct uv_gam_range_entry {
u32 limit; /* PA bits 56:26 (UV_GAM_RANGE_SHFT) */ u32 limit; /* PA bits 56:26 (UV_GAM_RANGE_SHFT) */
}; };
#define UV_AT_SIZE 8 /* 7 character arch type + NULL char */
struct uv_arch_type_entry {
char archtype[UV_AT_SIZE];
};
#define UV_SYSTAB_SIG "UVST" #define UV_SYSTAB_SIG "UVST"
#define UV_SYSTAB_VERSION_1 1 /* UV2/3 BIOS version */ #define UV_SYSTAB_VERSION_1 1 /* UV2/3 BIOS version */
#define UV_SYSTAB_VERSION_UV4 0x400 /* UV4 BIOS base version */ #define UV_SYSTAB_VERSION_UV4 0x400 /* UV4 BIOS base version */
...@@ -79,10 +85,14 @@ struct uv_gam_range_entry { ...@@ -79,10 +85,14 @@ struct uv_gam_range_entry {
#define UV_SYSTAB_VERSION_UV4_3 0x403 /* - GAM Range PXM Value */ #define UV_SYSTAB_VERSION_UV4_3 0x403 /* - GAM Range PXM Value */
#define UV_SYSTAB_VERSION_UV4_LATEST UV_SYSTAB_VERSION_UV4_3 #define UV_SYSTAB_VERSION_UV4_LATEST UV_SYSTAB_VERSION_UV4_3
#define UV_SYSTAB_VERSION_UV5 0x500 /* UV5 GAM base version */
#define UV_SYSTAB_VERSION_UV5_LATEST UV_SYSTAB_VERSION_UV5
#define UV_SYSTAB_TYPE_UNUSED 0 /* End of table (offset == 0) */ #define UV_SYSTAB_TYPE_UNUSED 0 /* End of table (offset == 0) */
#define UV_SYSTAB_TYPE_GAM_PARAMS 1 /* GAM PARAM conversions */ #define UV_SYSTAB_TYPE_GAM_PARAMS 1 /* GAM PARAM conversions */
#define UV_SYSTAB_TYPE_GAM_RNG_TBL 2 /* GAM entry table */ #define UV_SYSTAB_TYPE_GAM_RNG_TBL 2 /* GAM entry table */
#define UV_SYSTAB_TYPE_MAX 3 #define UV_SYSTAB_TYPE_ARCH_TYPE 3 /* UV arch type */
#define UV_SYSTAB_TYPE_MAX 4
/* /*
* The UV system table describes specific firmware * The UV system table describes specific firmware
...@@ -133,6 +143,7 @@ extern s64 uv_bios_reserved_page_pa(u64, u64 *, u64 *, u64 *); ...@@ -133,6 +143,7 @@ extern s64 uv_bios_reserved_page_pa(u64, u64 *, u64 *, u64 *);
extern int uv_bios_set_legacy_vga_target(bool decode, int domain, int bus); extern int uv_bios_set_legacy_vga_target(bool decode, int domain, int bus);
extern int uv_bios_init(void); extern int uv_bios_init(void);
extern unsigned long get_uv_systab_phys(bool msg);
extern unsigned long sn_rtc_cycles_per_second; extern unsigned long sn_rtc_cycles_per_second;
extern int uv_type; extern int uv_type;
......
...@@ -35,10 +35,8 @@ extern int is_uv_hubbed(int uvtype); ...@@ -35,10 +35,8 @@ extern int is_uv_hubbed(int uvtype);
extern void uv_cpu_init(void); extern void uv_cpu_init(void);
extern void uv_nmi_init(void); extern void uv_nmi_init(void);
extern void uv_system_init(void); extern void uv_system_init(void);
extern const struct cpumask *uv_flush_tlb_others(const struct cpumask *cpumask,
const struct flush_tlb_info *info);
#else /* X86_UV */ #else /* !X86_UV */
static inline enum uv_system_type get_uv_system_type(void) { return UV_NONE; } static inline enum uv_system_type get_uv_system_type(void) { return UV_NONE; }
static inline bool is_early_uv_system(void) { return 0; } static inline bool is_early_uv_system(void) { return 0; }
......
This diff is collapsed.
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
* *
* SGI UV architectural definitions * SGI UV architectural definitions
* *
* (C) Copyright 2020 Hewlett Packard Enterprise Development LP
* Copyright (C) 2007-2014 Silicon Graphics, Inc. All rights reserved. * Copyright (C) 2007-2014 Silicon Graphics, Inc. All rights reserved.
*/ */
...@@ -129,17 +130,6 @@ ...@@ -129,17 +130,6 @@
*/ */
#define UV_MAX_NASID_VALUE (UV_MAX_NUMALINK_BLADES * 2) #define UV_MAX_NASID_VALUE (UV_MAX_NUMALINK_BLADES * 2)
/* System Controller Interface Reg info */
struct uv_scir_s {
struct timer_list timer;
unsigned long offset;
unsigned long last;
unsigned long idle_on;
unsigned long idle_off;
unsigned char state;
unsigned char enabled;
};
/* GAM (globally addressed memory) range table */ /* GAM (globally addressed memory) range table */
struct uv_gam_range_s { struct uv_gam_range_s {
u32 limit; /* PA bits 56:26 (GAM_RANGE_SHFT) */ u32 limit; /* PA bits 56:26 (GAM_RANGE_SHFT) */
...@@ -155,6 +145,8 @@ struct uv_gam_range_s { ...@@ -155,6 +145,8 @@ struct uv_gam_range_s {
* available in the L3 cache on the cpu socket for the node. * available in the L3 cache on the cpu socket for the node.
*/ */
struct uv_hub_info_s { struct uv_hub_info_s {
unsigned int hub_type;
unsigned char hub_revision;
unsigned long global_mmr_base; unsigned long global_mmr_base;
unsigned long global_mmr_shift; unsigned long global_mmr_shift;
unsigned long gpa_mask; unsigned long gpa_mask;
...@@ -167,9 +159,9 @@ struct uv_hub_info_s { ...@@ -167,9 +159,9 @@ struct uv_hub_info_s {
unsigned char m_val; unsigned char m_val;
unsigned char n_val; unsigned char n_val;
unsigned char gr_table_len; unsigned char gr_table_len;
unsigned char hub_revision;
unsigned char apic_pnode_shift; unsigned char apic_pnode_shift;
unsigned char gpa_shift; unsigned char gpa_shift;
unsigned char nasid_shift;
unsigned char m_shift; unsigned char m_shift;
unsigned char n_lshift; unsigned char n_lshift;
unsigned int gnode_extra; unsigned int gnode_extra;
...@@ -191,16 +183,13 @@ struct uv_hub_info_s { ...@@ -191,16 +183,13 @@ struct uv_hub_info_s {
struct uv_cpu_info_s { struct uv_cpu_info_s {
void *p_uv_hub_info; void *p_uv_hub_info;
unsigned char blade_cpu_id; unsigned char blade_cpu_id;
struct uv_scir_s scir; void *reserved;
}; };
DECLARE_PER_CPU(struct uv_cpu_info_s, __uv_cpu_info); DECLARE_PER_CPU(struct uv_cpu_info_s, __uv_cpu_info);
#define uv_cpu_info this_cpu_ptr(&__uv_cpu_info) #define uv_cpu_info this_cpu_ptr(&__uv_cpu_info)
#define uv_cpu_info_per(cpu) (&per_cpu(__uv_cpu_info, cpu)) #define uv_cpu_info_per(cpu) (&per_cpu(__uv_cpu_info, cpu))
#define uv_scir_info (&uv_cpu_info->scir)
#define uv_cpu_scir_info(cpu) (&uv_cpu_info_per(cpu)->scir)
/* Node specific hub common info struct */ /* Node specific hub common info struct */
extern void **__uv_hub_info_list; extern void **__uv_hub_info_list;
static inline struct uv_hub_info_s *uv_hub_info_list(int node) static inline struct uv_hub_info_s *uv_hub_info_list(int node)
...@@ -219,6 +208,17 @@ static inline struct uv_hub_info_s *uv_cpu_hub_info(int cpu) ...@@ -219,6 +208,17 @@ static inline struct uv_hub_info_s *uv_cpu_hub_info(int cpu)
return (struct uv_hub_info_s *)uv_cpu_info_per(cpu)->p_uv_hub_info; return (struct uv_hub_info_s *)uv_cpu_info_per(cpu)->p_uv_hub_info;
} }
static inline int uv_hub_type(void)
{
return uv_hub_info->hub_type;
}
static inline __init void uv_hub_type_set(int uvmask)
{
uv_hub_info->hub_type = uvmask;
}
/* /*
* HUB revision ranges for each UV HUB architecture. * HUB revision ranges for each UV HUB architecture.
* This is a software convention - NOT the hardware revision numbers in * This is a software convention - NOT the hardware revision numbers in
...@@ -228,39 +228,31 @@ static inline struct uv_hub_info_s *uv_cpu_hub_info(int cpu) ...@@ -228,39 +228,31 @@ static inline struct uv_hub_info_s *uv_cpu_hub_info(int cpu)
#define UV3_HUB_REVISION_BASE 5 #define UV3_HUB_REVISION_BASE 5
#define UV4_HUB_REVISION_BASE 7 #define UV4_HUB_REVISION_BASE 7
#define UV4A_HUB_REVISION_BASE 8 /* UV4 (fixed) rev 2 */ #define UV4A_HUB_REVISION_BASE 8 /* UV4 (fixed) rev 2 */
#define UV5_HUB_REVISION_BASE 9
static inline int is_uv2_hub(void) static inline int is_uv(int uvmask) { return uv_hub_type() & uvmask; }
{ static inline int is_uv1_hub(void) { return 0; }
return is_uv_hubbed(uv(2)); static inline int is_uv2_hub(void) { return is_uv(UV2); }
} static inline int is_uv3_hub(void) { return is_uv(UV3); }
static inline int is_uv4a_hub(void) { return is_uv(UV4A); }
static inline int is_uv3_hub(void) static inline int is_uv4_hub(void) { return is_uv(UV4); }
{ static inline int is_uv5_hub(void) { return is_uv(UV5); }
return is_uv_hubbed(uv(3));
}
/* First test "is UV4A", then "is UV4" */ /*
static inline int is_uv4a_hub(void) * UV4A is a revision of UV4. So on UV4A, both is_uv4_hub() and
{ * is_uv4a_hub() return true, While on UV4, only is_uv4_hub()
if (is_uv_hubbed(uv(4))) * returns true. So to get true results, first test if is UV4A,
return (uv_hub_info->hub_revision == UV4A_HUB_REVISION_BASE); * then test if is UV4.
return 0; */
}
static inline int is_uv4_hub(void) /* UVX class: UV2,3,4 */
{ static inline int is_uvx_hub(void) { return is_uv(UVX); }
return is_uv_hubbed(uv(4));
}
static inline int is_uvx_hub(void) /* UVY class: UV5,..? */
{ static inline int is_uvy_hub(void) { return is_uv(UVY); }
return (is_uv_hubbed(-2) >= uv(2));
}
static inline int is_uv_hub(void) /* Any UV Hubbed System */
{ static inline int is_uv_hub(void) { return is_uv(UV_ANY); }
return is_uvx_hub();
}
union uvh_apicid { union uvh_apicid {
unsigned long v; unsigned long v;
...@@ -282,9 +274,11 @@ union uvh_apicid { ...@@ -282,9 +274,11 @@ union uvh_apicid {
* g - GNODE (full 15-bit global nasid, right shifted 1) * g - GNODE (full 15-bit global nasid, right shifted 1)
* p - PNODE (local part of nsids, right shifted 1) * p - PNODE (local part of nsids, right shifted 1)
*/ */
#define UV_NASID_TO_PNODE(n) (((n) >> 1) & uv_hub_info->pnode_mask) #define UV_NASID_TO_PNODE(n) \
(((n) >> uv_hub_info->nasid_shift) & uv_hub_info->pnode_mask)
#define UV_PNODE_TO_GNODE(p) ((p) |uv_hub_info->gnode_extra) #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_PNODE_TO_NASID(p) \
(UV_PNODE_TO_GNODE(p) << uv_hub_info->nasid_shift)
#define UV2_LOCAL_MMR_BASE 0xfa000000UL #define UV2_LOCAL_MMR_BASE 0xfa000000UL
#define UV2_GLOBAL_MMR32_BASE 0xfc000000UL #define UV2_GLOBAL_MMR32_BASE 0xfc000000UL
...@@ -297,29 +291,42 @@ union uvh_apicid { ...@@ -297,29 +291,42 @@ union uvh_apicid {
#define UV3_GLOBAL_MMR32_SIZE (32UL * 1024 * 1024) #define UV3_GLOBAL_MMR32_SIZE (32UL * 1024 * 1024)
#define UV4_LOCAL_MMR_BASE 0xfa000000UL #define UV4_LOCAL_MMR_BASE 0xfa000000UL
#define UV4_GLOBAL_MMR32_BASE 0xfc000000UL #define UV4_GLOBAL_MMR32_BASE 0
#define UV4_LOCAL_MMR_SIZE (32UL * 1024 * 1024) #define UV4_LOCAL_MMR_SIZE (32UL * 1024 * 1024)
#define UV4_GLOBAL_MMR32_SIZE (16UL * 1024 * 1024) #define UV4_GLOBAL_MMR32_SIZE 0
#define UV5_LOCAL_MMR_BASE 0xfa000000UL
#define UV5_GLOBAL_MMR32_BASE 0
#define UV5_LOCAL_MMR_SIZE (32UL * 1024 * 1024)
#define UV5_GLOBAL_MMR32_SIZE 0
#define UV_LOCAL_MMR_BASE ( \ #define UV_LOCAL_MMR_BASE ( \
is_uv2_hub() ? UV2_LOCAL_MMR_BASE : \ is_uv(UV2) ? UV2_LOCAL_MMR_BASE : \
is_uv3_hub() ? UV3_LOCAL_MMR_BASE : \ is_uv(UV3) ? UV3_LOCAL_MMR_BASE : \
/*is_uv4_hub*/ UV4_LOCAL_MMR_BASE) is_uv(UV4) ? UV4_LOCAL_MMR_BASE : \
is_uv(UV5) ? UV5_LOCAL_MMR_BASE : \
0)
#define UV_GLOBAL_MMR32_BASE ( \ #define UV_GLOBAL_MMR32_BASE ( \
is_uv2_hub() ? UV2_GLOBAL_MMR32_BASE : \ is_uv(UV2) ? UV2_GLOBAL_MMR32_BASE : \
is_uv3_hub() ? UV3_GLOBAL_MMR32_BASE : \ is_uv(UV3) ? UV3_GLOBAL_MMR32_BASE : \
/*is_uv4_hub*/ UV4_GLOBAL_MMR32_BASE) is_uv(UV4) ? UV4_GLOBAL_MMR32_BASE : \
is_uv(UV5) ? UV5_GLOBAL_MMR32_BASE : \
0)
#define UV_LOCAL_MMR_SIZE ( \ #define UV_LOCAL_MMR_SIZE ( \
is_uv2_hub() ? UV2_LOCAL_MMR_SIZE : \ is_uv(UV2) ? UV2_LOCAL_MMR_SIZE : \
is_uv3_hub() ? UV3_LOCAL_MMR_SIZE : \ is_uv(UV3) ? UV3_LOCAL_MMR_SIZE : \
/*is_uv4_hub*/ UV4_LOCAL_MMR_SIZE) is_uv(UV4) ? UV4_LOCAL_MMR_SIZE : \
is_uv(UV5) ? UV5_LOCAL_MMR_SIZE : \
0)
#define UV_GLOBAL_MMR32_SIZE ( \ #define UV_GLOBAL_MMR32_SIZE ( \
is_uv2_hub() ? UV2_GLOBAL_MMR32_SIZE : \ is_uv(UV2) ? UV2_GLOBAL_MMR32_SIZE : \
is_uv3_hub() ? UV3_GLOBAL_MMR32_SIZE : \ is_uv(UV3) ? UV3_GLOBAL_MMR32_SIZE : \
/*is_uv4_hub*/ UV4_GLOBAL_MMR32_SIZE) is_uv(UV4) ? UV4_GLOBAL_MMR32_SIZE : \
is_uv(UV5) ? UV5_GLOBAL_MMR32_SIZE : \
0)
#define UV_GLOBAL_MMR64_BASE (uv_hub_info->global_mmr_base) #define UV_GLOBAL_MMR64_BASE (uv_hub_info->global_mmr_base)
...@@ -720,7 +727,7 @@ extern void uv_nmi_setup_hubless(void); ...@@ -720,7 +727,7 @@ extern void uv_nmi_setup_hubless(void);
#define UVH_TSC_SYNC_SHIFT_UV2K 16 /* UV2/3k have different bits */ #define UVH_TSC_SYNC_SHIFT_UV2K 16 /* UV2/3k have different bits */
#define UVH_TSC_SYNC_MASK 3 /* 0011 */ #define UVH_TSC_SYNC_MASK 3 /* 0011 */
#define UVH_TSC_SYNC_VALID 3 /* 0011 */ #define UVH_TSC_SYNC_VALID 3 /* 0011 */
#define UVH_TSC_SYNC_INVALID 2 /* 0010 */ #define UVH_TSC_SYNC_UNKNOWN 0 /* 0000 */
/* BMC sets a bit this MMR non-zero before sending an NMI */ /* BMC sets a bit this MMR non-zero before sending an NMI */
#define UVH_NMI_MMR UVH_BIOS_KERNEL_MMR #define UVH_NMI_MMR UVH_BIOS_KERNEL_MMR
...@@ -728,19 +735,6 @@ extern void uv_nmi_setup_hubless(void); ...@@ -728,19 +735,6 @@ extern void uv_nmi_setup_hubless(void);
#define UVH_NMI_MMR_SHIFT 63 #define UVH_NMI_MMR_SHIFT 63
#define UVH_NMI_MMR_TYPE "SCRATCH5" #define UVH_NMI_MMR_TYPE "SCRATCH5"
/* Newer SMM NMI handler, not present in all systems */
#define UVH_NMI_MMRX UVH_EVENT_OCCURRED0
#define UVH_NMI_MMRX_CLEAR UVH_EVENT_OCCURRED0_ALIAS
#define UVH_NMI_MMRX_SHIFT UVH_EVENT_OCCURRED0_EXTIO_INT0_SHFT
#define UVH_NMI_MMRX_TYPE "EXTIO_INT0"
/* Non-zero indicates newer SMM NMI handler present */
#define UVH_NMI_MMRX_SUPPORTED UVH_EXTIO_INT0_BROADCAST
/* Indicates to BIOS that we want to use the newer SMM NMI handler */
#define UVH_NMI_MMRX_REQ UVH_BIOS_KERNEL_MMR_ALIAS_2
#define UVH_NMI_MMRX_REQ_SHIFT 62
struct uv_hub_nmi_s { struct uv_hub_nmi_s {
raw_spinlock_t nmi_lock; raw_spinlock_t nmi_lock;
atomic_t in_nmi; /* flag this node in UV NMI IRQ */ atomic_t in_nmi; /* flag this node in UV NMI IRQ */
...@@ -772,29 +766,6 @@ DECLARE_PER_CPU(struct uv_cpu_nmi_s, uv_cpu_nmi); ...@@ -772,29 +766,6 @@ DECLARE_PER_CPU(struct uv_cpu_nmi_s, uv_cpu_nmi);
#define UV_NMI_STATE_DUMP 2 #define UV_NMI_STATE_DUMP 2
#define UV_NMI_STATE_DUMP_DONE 3 #define UV_NMI_STATE_DUMP_DONE 3
/* Update SCIR state */
static inline void uv_set_scir_bits(unsigned char value)
{
if (uv_scir_info->state != value) {
uv_scir_info->state = value;
uv_write_local_mmr8(uv_scir_info->offset, value);
}
}
static inline unsigned long uv_scir_offset(int apicid)
{
return SCIR_LOCAL_MMR_BASE | (apicid & 0x3f);
}
static inline void uv_set_cpu_scir_bits(int cpu, unsigned char value)
{
if (uv_cpu_scir_info(cpu)->state != value) {
uv_write_global_mmr8(uv_cpu_to_pnode(cpu),
uv_cpu_scir_info(cpu)->offset, value);
uv_cpu_scir_info(cpu)->state = value;
}
}
/* /*
* Get the minimum revision number of the hub chips within the partition. * Get the minimum revision number of the hub chips within the partition.
* (See UVx_HUB_REVISION_BASE above for specific values.) * (See UVx_HUB_REVISION_BASE above for specific values.)
......
This diff is collapsed.
This diff is collapsed.
...@@ -148,9 +148,6 @@ static const __initconst struct idt_data apic_idts[] = { ...@@ -148,9 +148,6 @@ static const __initconst struct idt_data apic_idts[] = {
# endif # endif
# ifdef CONFIG_IRQ_WORK # ifdef CONFIG_IRQ_WORK
INTG(IRQ_WORK_VECTOR, asm_sysvec_irq_work), INTG(IRQ_WORK_VECTOR, asm_sysvec_irq_work),
# endif
# ifdef CONFIG_X86_UV
INTG(UV_BAU_MESSAGE, asm_sysvec_uv_bau_message),
# endif # endif
INTG(SPURIOUS_APIC_VECTOR, asm_sysvec_spurious_apic_interrupt), INTG(SPURIOUS_APIC_VECTOR, asm_sysvec_spurious_apic_interrupt),
INTG(ERROR_APIC_VECTOR, asm_sysvec_error_interrupt), INTG(ERROR_APIC_VECTOR, asm_sysvec_error_interrupt),
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
#include <asm/nospec-branch.h> #include <asm/nospec-branch.h>
#include <asm/cache.h> #include <asm/cache.h>
#include <asm/apic.h> #include <asm/apic.h>
#include <asm/uv/uv.h>
#include "mm_internal.h" #include "mm_internal.h"
...@@ -800,29 +799,6 @@ STATIC_NOPV void native_flush_tlb_others(const struct cpumask *cpumask, ...@@ -800,29 +799,6 @@ STATIC_NOPV void native_flush_tlb_others(const struct cpumask *cpumask,
trace_tlb_flush(TLB_REMOTE_SEND_IPI, trace_tlb_flush(TLB_REMOTE_SEND_IPI,
(info->end - info->start) >> PAGE_SHIFT); (info->end - info->start) >> PAGE_SHIFT);
if (is_uv_system()) {
/*
* This whole special case is confused. UV has a "Broadcast
* Assist Unit", which seems to be a fancy way to send IPIs.
* Back when x86 used an explicit TLB flush IPI, UV was
* optimized to use its own mechanism. These days, x86 uses
* smp_call_function_many(), but UV still uses a manual IPI,
* and that IPI's action is out of date -- it does a manual
* flush instead of calling flush_tlb_func_remote(). This
* means that the percpu tlb_gen variables won't be updated
* and we'll do pointless flushes on future context switches.
*
* Rather than hooking native_flush_tlb_others() here, I think
* that UV should be updated so that smp_call_function_many(),
* etc, are optimal on UV.
*/
cpumask = uv_flush_tlb_others(cpumask, info);
if (cpumask)
smp_call_function_many(cpumask, flush_tlb_func_remote,
(void *)info, 1);
return;
}
/* /*
* If no page tables were freed, we can skip sending IPIs to * If no page tables were freed, we can skip sending IPIs to
* CPUs in lazy TLB mode. They will flush the CPU themselves * CPUs in lazy TLB mode. They will flush the CPU themselves
......
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_X86_UV) += tlb_uv.o bios_uv.o uv_irq.o uv_sysfs.o uv_time.o uv_nmi.o obj-$(CONFIG_X86_UV) += bios_uv.o uv_irq.o uv_sysfs.o uv_time.o uv_nmi.o
...@@ -2,8 +2,9 @@ ...@@ -2,8 +2,9 @@
/* /*
* BIOS run time interface routines. * BIOS run time interface routines.
* *
* Copyright (c) 2008-2009 Silicon Graphics, Inc. All Rights Reserved. * (C) Copyright 2020 Hewlett Packard Enterprise Development LP
* Copyright (c) Russ Anderson <rja@sgi.com> * Copyright (C) 2007-2017 Silicon Graphics, Inc. All rights reserved.
* Copyright (c) Russ Anderson <rja@sgi.com>
*/ */
#include <linux/efi.h> #include <linux/efi.h>
...@@ -170,16 +171,27 @@ int uv_bios_set_legacy_vga_target(bool decode, int domain, int bus) ...@@ -170,16 +171,27 @@ int uv_bios_set_legacy_vga_target(bool decode, int domain, int bus)
(u64)decode, (u64)domain, (u64)bus, 0, 0); (u64)decode, (u64)domain, (u64)bus, 0, 0);
} }
int uv_bios_init(void) unsigned long get_uv_systab_phys(bool msg)
{ {
uv_systab = NULL;
if ((uv_systab_phys == EFI_INVALID_TABLE_ADDR) || if ((uv_systab_phys == EFI_INVALID_TABLE_ADDR) ||
!uv_systab_phys || efi_runtime_disabled()) { !uv_systab_phys || efi_runtime_disabled()) {
pr_crit("UV: UVsystab: missing\n"); if (msg)
return -EEXIST; pr_crit("UV: UVsystab: missing\n");
return 0;
} }
return uv_systab_phys;
}
int uv_bios_init(void)
{
unsigned long uv_systab_phys_addr;
uv_systab = NULL;
uv_systab_phys_addr = get_uv_systab_phys(1);
if (!uv_systab_phys_addr)
return -EEXIST;
uv_systab = ioremap(uv_systab_phys, sizeof(struct uv_systab)); uv_systab = ioremap(uv_systab_phys_addr, sizeof(struct uv_systab));
if (!uv_systab || strncmp(uv_systab->signature, UV_SYSTAB_SIG, 4)) { if (!uv_systab || strncmp(uv_systab->signature, UV_SYSTAB_SIG, 4)) {
pr_err("UV: UVsystab: bad signature!\n"); pr_err("UV: UVsystab: bad signature!\n");
iounmap(uv_systab); iounmap(uv_systab);
...@@ -191,7 +203,7 @@ int uv_bios_init(void) ...@@ -191,7 +203,7 @@ int uv_bios_init(void)
int size = uv_systab->size; int size = uv_systab->size;
iounmap(uv_systab); iounmap(uv_systab);
uv_systab = ioremap(uv_systab_phys, size); uv_systab = ioremap(uv_systab_phys_addr, size);
if (!uv_systab) { if (!uv_systab) {
pr_err("UV: UVsystab: ioremap(%d) failed!\n", size); pr_err("UV: UVsystab: ioremap(%d) failed!\n", size);
return -EFAULT; return -EFAULT;
......
This diff is collapsed.
...@@ -2,8 +2,9 @@ ...@@ -2,8 +2,9 @@
/* /*
* SGI NMI support routines * SGI NMI support routines
* *
* Copyright (c) 2009-2013 Silicon Graphics, Inc. All Rights Reserved. * (C) Copyright 2020 Hewlett Packard Enterprise Development LP
* Copyright (c) Mike Travis * Copyright (C) 2007-2017 Silicon Graphics, Inc. All rights reserved.
* Copyright (c) Mike Travis
*/ */
#include <linux/cpu.h> #include <linux/cpu.h>
...@@ -54,6 +55,20 @@ static struct uv_hub_nmi_s **uv_hub_nmi_list; ...@@ -54,6 +55,20 @@ static struct uv_hub_nmi_s **uv_hub_nmi_list;
DEFINE_PER_CPU(struct uv_cpu_nmi_s, uv_cpu_nmi); DEFINE_PER_CPU(struct uv_cpu_nmi_s, uv_cpu_nmi);
/* Newer SMM NMI handler, not present in all systems */
static unsigned long uvh_nmi_mmrx; /* UVH_EVENT_OCCURRED0/1 */
static unsigned long uvh_nmi_mmrx_clear; /* UVH_EVENT_OCCURRED0/1_ALIAS */
static int uvh_nmi_mmrx_shift; /* UVH_EVENT_OCCURRED0/1_EXTIO_INT0_SHFT */
static int uvh_nmi_mmrx_mask; /* UVH_EVENT_OCCURRED0/1_EXTIO_INT0_MASK */
static char *uvh_nmi_mmrx_type; /* "EXTIO_INT0" */
/* Non-zero indicates newer SMM NMI handler present */
static unsigned long uvh_nmi_mmrx_supported; /* UVH_EXTIO_INT0_BROADCAST */
/* Indicates to BIOS that we want to use the newer SMM NMI handler */
static unsigned long uvh_nmi_mmrx_req; /* UVH_BIOS_KERNEL_MMR_ALIAS_2 */
static int uvh_nmi_mmrx_req_shift; /* 62 */
/* UV hubless values */ /* UV hubless values */
#define NMI_CONTROL_PORT 0x70 #define NMI_CONTROL_PORT 0x70
#define NMI_DUMMY_PORT 0x71 #define NMI_DUMMY_PORT 0x71
...@@ -227,13 +242,43 @@ static inline bool uv_nmi_action_is(const char *action) ...@@ -227,13 +242,43 @@ static inline bool uv_nmi_action_is(const char *action)
/* Setup which NMI support is present in system */ /* Setup which NMI support is present in system */
static void uv_nmi_setup_mmrs(void) static void uv_nmi_setup_mmrs(void)
{ {
if (uv_read_local_mmr(UVH_NMI_MMRX_SUPPORTED)) { /* First determine arch specific MMRs to handshake with BIOS */
uv_write_local_mmr(UVH_NMI_MMRX_REQ, if (UVH_EVENT_OCCURRED0_EXTIO_INT0_MASK) {
1UL << UVH_NMI_MMRX_REQ_SHIFT); uvh_nmi_mmrx = UVH_EVENT_OCCURRED0;
nmi_mmr = UVH_NMI_MMRX; uvh_nmi_mmrx_clear = UVH_EVENT_OCCURRED0_ALIAS;
nmi_mmr_clear = UVH_NMI_MMRX_CLEAR; uvh_nmi_mmrx_shift = UVH_EVENT_OCCURRED0_EXTIO_INT0_SHFT;
nmi_mmr_pending = 1UL << UVH_NMI_MMRX_SHIFT; uvh_nmi_mmrx_mask = UVH_EVENT_OCCURRED0_EXTIO_INT0_MASK;
pr_info("UV: SMI NMI support: %s\n", UVH_NMI_MMRX_TYPE); uvh_nmi_mmrx_type = "OCRD0-EXTIO_INT0";
uvh_nmi_mmrx_supported = UVH_EXTIO_INT0_BROADCAST;
uvh_nmi_mmrx_req = UVH_BIOS_KERNEL_MMR_ALIAS_2;
uvh_nmi_mmrx_req_shift = 62;
} else if (UVH_EVENT_OCCURRED1_EXTIO_INT0_MASK) {
uvh_nmi_mmrx = UVH_EVENT_OCCURRED1;
uvh_nmi_mmrx_clear = UVH_EVENT_OCCURRED1_ALIAS;
uvh_nmi_mmrx_shift = UVH_EVENT_OCCURRED1_EXTIO_INT0_SHFT;
uvh_nmi_mmrx_mask = UVH_EVENT_OCCURRED1_EXTIO_INT0_MASK;
uvh_nmi_mmrx_type = "OCRD1-EXTIO_INT0";
uvh_nmi_mmrx_supported = UVH_EXTIO_INT0_BROADCAST;
uvh_nmi_mmrx_req = UVH_BIOS_KERNEL_MMR_ALIAS_2;
uvh_nmi_mmrx_req_shift = 62;
} else {
pr_err("UV:%s:cannot find EVENT_OCCURRED*_EXTIO_INT0\n",
__func__);
return;
}
/* Then find out if new NMI is supported */
if (likely(uv_read_local_mmr(uvh_nmi_mmrx_supported))) {
uv_write_local_mmr(uvh_nmi_mmrx_req,
1UL << uvh_nmi_mmrx_req_shift);
nmi_mmr = uvh_nmi_mmrx;
nmi_mmr_clear = uvh_nmi_mmrx_clear;
nmi_mmr_pending = 1UL << uvh_nmi_mmrx_shift;
pr_info("UV: SMI NMI support: %s\n", uvh_nmi_mmrx_type);
} else { } else {
nmi_mmr = UVH_NMI_MMR; nmi_mmr = UVH_NMI_MMR;
nmi_mmr_clear = UVH_NMI_MMR_CLEAR; nmi_mmr_clear = UVH_NMI_MMR_CLEAR;
...@@ -1049,5 +1094,5 @@ void __init uv_nmi_setup_hubless(void) ...@@ -1049,5 +1094,5 @@ void __init uv_nmi_setup_hubless(void)
/* Ensure NMI enabled in Processor Interface Reg: */ /* Ensure NMI enabled in Processor Interface Reg: */
uv_reassert_nmi(); uv_reassert_nmi();
uv_register_nmi_notifier(); uv_register_nmi_notifier();
pr_info("UV: Hubless NMI enabled\n"); pr_info("UV: PCH NMI enabled\n");
} }
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
/* /*
* SGI RTC clock/timer routines. * SGI RTC clock/timer routines.
* *
* (C) Copyright 2020 Hewlett Packard Enterprise Development LP
* Copyright (c) 2009-2013 Silicon Graphics, Inc. All Rights Reserved. * Copyright (c) 2009-2013 Silicon Graphics, Inc. All Rights Reserved.
* Copyright (c) Dimitri Sivanich * Copyright (c) Dimitri Sivanich
*/ */
...@@ -52,7 +53,7 @@ struct uv_rtc_timer_head { ...@@ -52,7 +53,7 @@ struct uv_rtc_timer_head {
struct { struct {
int lcpu; /* systemwide logical cpu number */ int lcpu; /* systemwide logical cpu number */
u64 expires; /* next timer expiration for this cpu */ u64 expires; /* next timer expiration for this cpu */
} cpu[1]; } cpu[];
}; };
/* /*
...@@ -84,10 +85,8 @@ static void uv_rtc_send_IPI(int cpu) ...@@ -84,10 +85,8 @@ static void uv_rtc_send_IPI(int cpu)
/* Check for an RTC interrupt pending */ /* Check for an RTC interrupt pending */
static int uv_intr_pending(int pnode) static int uv_intr_pending(int pnode)
{ {
if (is_uvx_hub()) return uv_read_global_mmr64(pnode, UVH_EVENT_OCCURRED2) &
return uv_read_global_mmr64(pnode, UVXH_EVENT_OCCURRED2) & UVH_EVENT_OCCURRED2_RTC_1_MASK;
UVXH_EVENT_OCCURRED2_RTC_1_MASK;
return 0;
} }
/* Setup interrupt and return non-zero if early expiration occurred. */ /* Setup interrupt and return non-zero if early expiration occurred. */
...@@ -101,8 +100,8 @@ static int uv_setup_intr(int cpu, u64 expires) ...@@ -101,8 +100,8 @@ static int uv_setup_intr(int cpu, u64 expires)
UVH_RTC1_INT_CONFIG_M_MASK); UVH_RTC1_INT_CONFIG_M_MASK);
uv_write_global_mmr64(pnode, UVH_INT_CMPB, -1L); uv_write_global_mmr64(pnode, UVH_INT_CMPB, -1L);
uv_write_global_mmr64(pnode, UVXH_EVENT_OCCURRED2_ALIAS, uv_write_global_mmr64(pnode, UVH_EVENT_OCCURRED2_ALIAS,
UVXH_EVENT_OCCURRED2_RTC_1_MASK); UVH_EVENT_OCCURRED2_RTC_1_MASK);
val = (X86_PLATFORM_IPI_VECTOR << UVH_RTC1_INT_CONFIG_VECTOR_SHFT) | val = (X86_PLATFORM_IPI_VECTOR << UVH_RTC1_INT_CONFIG_VECTOR_SHFT) |
((u64)apicid << UVH_RTC1_INT_CONFIG_APIC_ID_SHFT); ((u64)apicid << UVH_RTC1_INT_CONFIG_APIC_ID_SHFT);
...@@ -148,9 +147,8 @@ static __init int uv_rtc_allocate_timers(void) ...@@ -148,9 +147,8 @@ static __init int uv_rtc_allocate_timers(void)
struct uv_rtc_timer_head *head = blade_info[bid]; struct uv_rtc_timer_head *head = blade_info[bid];
if (!head) { if (!head) {
head = kmalloc_node(sizeof(struct uv_rtc_timer_head) + head = kmalloc_node(struct_size(head, cpu,
(uv_blade_nr_possible_cpus(bid) * uv_blade_nr_possible_cpus(bid)),
2 * sizeof(u64)),
GFP_KERNEL, nid); GFP_KERNEL, nid);
if (!head) { if (!head) {
uv_rtc_deallocate_timers(); uv_rtc_deallocate_timers();
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
* This file supports the user system call for file open, close, mmap, etc. * This file supports the user system call for file open, close, mmap, etc.
* This also incudes the driver initialization code. * This also incudes the driver initialization code.
* *
* (C) Copyright 2020 Hewlett Packard Enterprise Development LP
* Copyright (c) 2008-2014 Silicon Graphics, Inc. All Rights Reserved. * Copyright (c) 2008-2014 Silicon Graphics, Inc. All Rights Reserved.
*/ */
...@@ -516,7 +517,7 @@ static int __init gru_init(void) ...@@ -516,7 +517,7 @@ static int __init gru_init(void)
#if defined CONFIG_IA64 #if defined CONFIG_IA64
gru_start_paddr = 0xd000000000UL; /* ZZZZZZZZZZZZZZZZZZZ fixme */ gru_start_paddr = 0xd000000000UL; /* ZZZZZZZZZZZZZZZZZZZ fixme */
#else #else
gru_start_paddr = uv_read_local_mmr(UVH_RH_GAM_GRU_OVERLAY_CONFIG_MMR) & gru_start_paddr = uv_read_local_mmr(UVH_RH_GAM_GRU_OVERLAY_CONFIG) &
0x7fffffffffffUL; 0x7fffffffffffUL;
#endif #endif
gru_start_vaddr = __va(gru_start_paddr); gru_start_vaddr = __va(gru_start_paddr);
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* License. See the file "COPYING" in the main directory of this archive * License. See the file "COPYING" in the main directory of this archive
* for more details. * for more details.
* *
* (C) Copyright 2020 Hewlett Packard Enterprise Development LP
* Copyright (C) 2004-2008 Silicon Graphics, Inc. All rights reserved. * Copyright (C) 2004-2008 Silicon Graphics, Inc. All rights reserved.
*/ */
...@@ -17,11 +18,6 @@ ...@@ -17,11 +18,6 @@
#if defined CONFIG_X86_UV || defined CONFIG_IA64_SGI_UV #if defined CONFIG_X86_UV || defined CONFIG_IA64_SGI_UV
#include <asm/uv/uv.h> #include <asm/uv/uv.h>
#define is_uv() is_uv_system()
#endif
#ifndef is_uv
#define is_uv() 0
#endif #endif
#ifdef USE_DBUG_ON #ifdef USE_DBUG_ON
...@@ -79,7 +75,7 @@ ...@@ -79,7 +75,7 @@
#define XPC_MSG_SIZE(_payload_size) \ #define XPC_MSG_SIZE(_payload_size) \
ALIGN(XPC_MSG_HDR_MAX_SIZE + (_payload_size), \ ALIGN(XPC_MSG_HDR_MAX_SIZE + (_payload_size), \
is_uv() ? 64 : 128) is_uv_system() ? 64 : 128)
/* /*
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* License. See the file "COPYING" in the main directory of this archive * License. See the file "COPYING" in the main directory of this archive
* for more details. * for more details.
* *
* (C) Copyright 2020 Hewlett Packard Enterprise Development LP
* Copyright (c) 2004-2008 Silicon Graphics, Inc. All Rights Reserved. * Copyright (c) 2004-2008 Silicon Graphics, Inc. All Rights Reserved.
*/ */
...@@ -233,7 +234,7 @@ xp_init(void) ...@@ -233,7 +234,7 @@ xp_init(void)
for (ch_number = 0; ch_number < XPC_MAX_NCHANNELS; ch_number++) for (ch_number = 0; ch_number < XPC_MAX_NCHANNELS; ch_number++)
mutex_init(&xpc_registrations[ch_number].mutex); mutex_init(&xpc_registrations[ch_number].mutex);
if (is_uv()) if (is_uv_system())
ret = xp_init_uv(); ret = xp_init_uv();
else else
ret = 0; ret = 0;
...@@ -249,7 +250,7 @@ module_init(xp_init); ...@@ -249,7 +250,7 @@ module_init(xp_init);
static void __exit static void __exit
xp_exit(void) xp_exit(void)
{ {
if (is_uv()) if (is_uv_system())
xp_exit_uv(); xp_exit_uv();
} }
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* License. See the file "COPYING" in the main directory of this archive * License. See the file "COPYING" in the main directory of this archive
* for more details. * for more details.
* *
* (C) Copyright 2020 Hewlett Packard Enterprise Development LP
* Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
*/ */
...@@ -148,7 +149,9 @@ xp_restrict_memprotect_uv(unsigned long phys_addr, unsigned long size) ...@@ -148,7 +149,9 @@ xp_restrict_memprotect_uv(unsigned long phys_addr, unsigned long size)
enum xp_retval enum xp_retval
xp_init_uv(void) xp_init_uv(void)
{ {
BUG_ON(!is_uv()); WARN_ON(!is_uv_system());
if (!is_uv_system())
return xpUnsupported;
xp_max_npartitions = XP_MAX_NPARTITIONS_UV; xp_max_npartitions = XP_MAX_NPARTITIONS_UV;
#ifdef CONFIG_X86 #ifdef CONFIG_X86
...@@ -168,5 +171,5 @@ xp_init_uv(void) ...@@ -168,5 +171,5 @@ xp_init_uv(void)
void void
xp_exit_uv(void) xp_exit_uv(void)
{ {
BUG_ON(!is_uv()); WARN_ON(!is_uv_system());
} }
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* License. See the file "COPYING" in the main directory of this archive * License. See the file "COPYING" in the main directory of this archive
* for more details. * for more details.
* *
* (C) Copyright 2020 Hewlett Packard Enterprise Development LP
* Copyright (c) 2004-2009 Silicon Graphics, Inc. All Rights Reserved. * Copyright (c) 2004-2009 Silicon Graphics, Inc. All Rights Reserved.
*/ */
...@@ -1043,7 +1044,7 @@ xpc_do_exit(enum xp_retval reason) ...@@ -1043,7 +1044,7 @@ xpc_do_exit(enum xp_retval reason)
xpc_teardown_partitions(); xpc_teardown_partitions();
if (is_uv()) if (is_uv_system())
xpc_exit_uv(); xpc_exit_uv();
} }
...@@ -1226,7 +1227,7 @@ xpc_init(void) ...@@ -1226,7 +1227,7 @@ xpc_init(void)
dev_set_name(xpc_part, "part"); dev_set_name(xpc_part, "part");
dev_set_name(xpc_chan, "chan"); dev_set_name(xpc_chan, "chan");
if (is_uv()) { if (is_uv_system()) {
ret = xpc_init_uv(); ret = xpc_init_uv();
} else { } else {
...@@ -1312,7 +1313,7 @@ xpc_init(void) ...@@ -1312,7 +1313,7 @@ xpc_init(void)
xpc_teardown_partitions(); xpc_teardown_partitions();
out_1: out_1:
if (is_uv()) if (is_uv_system())
xpc_exit_uv(); xpc_exit_uv();
return ret; return ret;
} }
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* License. See the file "COPYING" in the main directory of this archive * License. See the file "COPYING" in the main directory of this archive
* for more details. * for more details.
* *
* (C) Copyright 2020 Hewlett Packard Enterprise Development LP
* Copyright (c) 2004-2008 Silicon Graphics, Inc. All Rights Reserved. * Copyright (c) 2004-2008 Silicon Graphics, Inc. All Rights Reserved.
*/ */
...@@ -433,7 +434,7 @@ xpc_discovery(void) ...@@ -433,7 +434,7 @@ xpc_discovery(void)
*/ */
region_size = xp_region_size; region_size = xp_region_size;
if (is_uv()) if (is_uv_system())
max_regions = 256; max_regions = 256;
else { else {
max_regions = 64; max_regions = 64;
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* License. See the file "COPYING" in the main directory of this archive * License. See the file "COPYING" in the main directory of this archive
* for more details. * for more details.
* *
* (C) Copyright 2020 Hewlett Packard Enterprise Development LP
* Copyright (C) 1999-2009 Silicon Graphics, Inc. All rights reserved. * Copyright (C) 1999-2009 Silicon Graphics, Inc. All rights reserved.
*/ */
...@@ -515,7 +516,7 @@ xpnet_init(void) ...@@ -515,7 +516,7 @@ xpnet_init(void)
{ {
int result; int result;
if (!is_uv()) if (!is_uv_system())
return -ENODEV; return -ENODEV;
dev_info(xpnet, "registering network device %s\n", XPNET_DEVICE_NAME); dev_info(xpnet, "registering network device %s\n", XPNET_DEVICE_NAME);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment