/*
* Copyright 2010 Tilera Corporation. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, version 2.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for
* more details.
*/
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/rwsem.h>
#include <linux/kprobes.h>
#include <linux/sched.h>
#include <linux/hardirq.h>
#include <linux/uaccess.h>
#include <linux/smp.h>
#include <linux/cdev.h>
#include <linux/compat.h>
#include <asm/hardwall.h>
#include <asm/traps.h>
#include <asm/siginfo.h>
#include <asm/irq_regs.h>
#include <arch/interrupts.h>
#include <arch/spr_def.h>
/*
* Implement a per-cpu "hardwall" resource class such as UDN or IPI.
* We use "hardwall" nomenclature throughout for historical reasons.
* The lock here controls access to the list data structure as well as
* to the items on the list.
*/
struct hardwall_type {
int index;
int is_xdn;
int is_idn;
int disabled;
const char *name;
struct list_head list;
spinlock_t lock;
struct proc_dir_entry *proc_dir;
};
enum hardwall_index {
HARDWALL_UDN = 0,
#ifndef __tilepro__
HARDWALL_IDN = 1,
HARDWALL_IPI = 2,
#endif
_HARDWALL_TYPES
};
static struct hardwall_type hardwall_types[] = {
{ /* user-space access to UDN */
0,
1,
0,
0,
"udn",
LIST_HEAD_INIT(hardwall_types[HARDWALL_UDN].list),
__SPIN_LOCK_INITIALIZER(hardwall_types[HARDWALL_UDN].lock),
NULL
},
#ifndef __tilepro__
{ /* user-space access to IDN */
1,
1,
1,
1, /* disabled pending hypervisor support */
"idn",
LIST_HEAD_INIT(hardwall_types[HARDWALL_IDN].list),
__SPIN_LOCK_INITIALIZER(hardwall_types[HARDWALL_IDN].lock),
NULL
},
{ /* access to user-space IPI */
2,
0,
0,
0,
"ipi",
LIST_HEAD_INIT(hardwall_types[HARDWALL_IPI].list),
__SPIN_LOCK_INITIALIZER(hardwall_types[HARDWALL_IPI].lock),
NULL
},
#endif
};
/*
* This data structure tracks the cpu data, etc., associated
* one-to-one with a "struct file *" from opening a hardwall device file.
* Note that the file's private data points back to this structure.
*/
struct hardwall_info {
struct list_head list; /* for hardwall_types.list */
struct list_head task_head; /* head of tasks in this hardwall */
struct hardwall_type *type; /* type of this resource */
struct cpumask cpumask; /* cpus reserved */
int id; /* integer id for this hardwall */
int teardown_in_progress; /* are we tearing this one down? */
/* Remaining fields only valid for user-network resources. */
int ulhc_x; /* upper left hand corner x coord */
int ulhc_y; /* upper left hand corner y coord */
int width; /* rectangle width */
int height; /* rectangle height */
#if CHIP_HAS_REV1_XDN()
atomic_t xdn_pending_count; /* cores in phase 1 of drain */
#endif
};
/* /proc/tile/hardwall */
static struct proc_dir_entry *hardwall_proc_dir;
/* Functions to manage files in /proc/tile/hardwall. */
static void hardwall_add_proc(struct hardwall_info *);
static void hardwall_remove_proc(struct hardwall_info *);
/* Allow disabling UDN access. */
static int __init noudn(char *str)
{
pr_info("User-space UDN access is disabled\n");
hardwall_types[HARDWALL_UDN].disabled = 1;
return 0;
}
early_param("noudn", noudn);
#ifndef __tilepro__
/* Allow disabling IDN access. */
static int __init noidn(char *str)
{
pr_info("User-space IDN access is disabled\n");
hardwall_types[HARDWALL_IDN].disabled = 1;
return 0;
}
early_param("noidn", noidn);
/* Allow disabling IPI access. */
static int __init noipi(char *str)
{
pr_info("User-space IPI access is disabled\n");
hardwall_types[HARDWALL_IPI].disabled = 1;
return 0;
}
early_param("noipi", noipi);
#endif
/*
* Low-level primitives for UDN/IDN
*/
#ifdef __tilepro__
#define mtspr_XDN(hwt, name, val) \
do { (void)(hwt); __insn_mtspr(SPR_UDN_##name, (val)); } while (0)
#define mtspr_MPL_XDN(hwt, name, val) \
do { (void)(hwt); __insn_mtspr(SPR_MPL_UDN_##name, (val)); } while (0)
#define mfspr_XDN(hwt, name) \
((void)(hwt), __insn_mfspr(SPR_UDN_##name))
#else
#define mtspr_XDN(hwt, name, val) \
do { \
if ((hwt)->is_idn) \
__insn_mtspr(SPR_IDN_##name, (val)); \
else \
__insn_mtspr(SPR_UDN_##name, (val)); \
} while (0)
#define mtspr_MPL_XDN(hwt, name, val) \
do { \
if ((hwt)->is_idn) \
__insn_mtspr(SPR_MPL_IDN_##name, (val)); \
else \
__insn_mtspr(SPR_MPL_UDN_##name, (val)); \
} while (0)
#define mfspr_XDN(hwt, name) \
((hwt)->is_idn ? __insn_mfspr(SPR_IDN_##name) : __insn_mfspr(SPR_UDN_##name))
#endif
/* Set a CPU bit if the CPU is online. */
#define cpu_online_set(cpu, dst) do { \
if (cpu_online(cpu)) \
cpumask_set_cpu(cpu, dst); \
} while (0)
/* Does the given rectangle contain the given x,y coordinate? */