-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add SMP support for metag. This allows Linux to take control of multiple hardware threads on a single Meta core, treating them as separate Linux CPUs. Signed-off-by: James Hogan <james.hogan@imgtec.com>
- Loading branch information
James Hogan
committed
Mar 2, 2013
1 parent
fdabf52
commit 42682c6
Showing
9 changed files
with
1,064 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/* | ||
* Meta cache partition manipulation. | ||
* | ||
* Copyright 2010 Imagination Technologies Ltd. | ||
*/ | ||
|
||
#ifndef _METAG_CACHEPART_H_ | ||
#define _METAG_CACHEPART_H_ | ||
|
||
/** | ||
* get_dcache_size() - Get size of data cache. | ||
*/ | ||
unsigned int get_dcache_size(void); | ||
|
||
/** | ||
* get_icache_size() - Get size of code cache. | ||
*/ | ||
unsigned int get_icache_size(void); | ||
|
||
/** | ||
* get_global_dcache_size() - Get the thread's global dcache. | ||
* | ||
* Returns the size of the current thread's global dcache partition. | ||
*/ | ||
unsigned int get_global_dcache_size(void); | ||
|
||
/** | ||
* get_global_icache_size() - Get the thread's global icache. | ||
* | ||
* Returns the size of the current thread's global icache partition. | ||
*/ | ||
unsigned int get_global_icache_size(void); | ||
|
||
/** | ||
* check_for_dache_aliasing() - Ensure that the bootloader has configured the | ||
* dache and icache properly to avoid aliasing | ||
* @thread_id: Hardware thread ID | ||
* | ||
*/ | ||
void check_for_cache_aliasing(int thread_id); | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
#ifndef __ASM_METAG_CORE_REG_H_ | ||
#define __ASM_METAG_CORE_REG_H_ | ||
|
||
#include <asm/metag_regs.h> | ||
|
||
extern void core_reg_write(int unit, int reg, int thread, unsigned int val); | ||
extern unsigned int core_reg_read(int unit, int reg, int thread); | ||
|
||
/* | ||
* These macros allow direct access from C to any register known to the | ||
* assembler. Example candidates are TXTACTCYC, TXIDLECYC, and TXPRIVEXT. | ||
*/ | ||
|
||
#define __core_reg_get(reg) ({ \ | ||
unsigned int __grvalue; \ | ||
asm volatile("MOV %0," #reg \ | ||
: "=r" (__grvalue)); \ | ||
__grvalue; \ | ||
}) | ||
|
||
#define __core_reg_set(reg, value) do { \ | ||
unsigned int __srvalue = (value); \ | ||
asm volatile("MOV " #reg ",%0" \ | ||
: \ | ||
: "r" (__srvalue)); \ | ||
} while (0) | ||
|
||
#define __core_reg_swap(reg, value) do { \ | ||
unsigned int __srvalue = (value); \ | ||
asm volatile("SWAP " #reg ",%0" \ | ||
: "+r" (__srvalue)); \ | ||
(value) = __srvalue; \ | ||
} while (0) | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#ifndef __ASM_SMP_H | ||
#define __ASM_SMP_H | ||
|
||
#include <linux/cpumask.h> | ||
|
||
#define raw_smp_processor_id() (current_thread_info()->cpu) | ||
|
||
enum ipi_msg_type { | ||
IPI_CALL_FUNC, | ||
IPI_CALL_FUNC_SINGLE, | ||
IPI_RESCHEDULE, | ||
}; | ||
|
||
extern void arch_send_call_function_single_ipi(int cpu); | ||
extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); | ||
#define arch_send_call_function_ipi_mask arch_send_call_function_ipi_mask | ||
|
||
asmlinkage void secondary_start_kernel(void); | ||
|
||
extern void secondary_startup(void); | ||
|
||
#ifdef CONFIG_HOTPLUG_CPU | ||
extern void __cpu_die(unsigned int cpu); | ||
extern int __cpu_disable(void); | ||
extern void cpu_die(void); | ||
#endif | ||
|
||
extern void smp_init_cpus(void); | ||
#endif /* __ASM_SMP_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
#ifndef _ASM_METAG_TOPOLOGY_H | ||
#define _ASM_METAG_TOPOLOGY_H | ||
|
||
#ifdef CONFIG_NUMA | ||
|
||
/* sched_domains SD_NODE_INIT for Meta machines */ | ||
#define SD_NODE_INIT (struct sched_domain) { \ | ||
.parent = NULL, \ | ||
.child = NULL, \ | ||
.groups = NULL, \ | ||
.min_interval = 8, \ | ||
.max_interval = 32, \ | ||
.busy_factor = 32, \ | ||
.imbalance_pct = 125, \ | ||
.cache_nice_tries = 2, \ | ||
.busy_idx = 3, \ | ||
.idle_idx = 2, \ | ||
.newidle_idx = 0, \ | ||
.wake_idx = 0, \ | ||
.forkexec_idx = 0, \ | ||
.flags = SD_LOAD_BALANCE \ | ||
| SD_BALANCE_FORK \ | ||
| SD_BALANCE_EXEC \ | ||
| SD_BALANCE_NEWIDLE \ | ||
| SD_SERIALIZE, \ | ||
.last_balance = jiffies, \ | ||
.balance_interval = 1, \ | ||
.nr_balance_failed = 0, \ | ||
} | ||
|
||
#define cpu_to_node(cpu) ((void)(cpu), 0) | ||
#define parent_node(node) ((void)(node), 0) | ||
|
||
#define cpumask_of_node(node) ((void)node, cpu_online_mask) | ||
|
||
#define pcibus_to_node(bus) ((void)(bus), -1) | ||
#define cpumask_of_pcibus(bus) (pcibus_to_node(bus) == -1 ? \ | ||
cpu_all_mask : \ | ||
cpumask_of_node(pcibus_to_node(bus))) | ||
|
||
#endif | ||
|
||
#define mc_capable() (1) | ||
|
||
const struct cpumask *cpu_coregroup_mask(unsigned int cpu); | ||
|
||
extern cpumask_t cpu_core_map[NR_CPUS]; | ||
|
||
#define topology_core_cpumask(cpu) (&cpu_core_map[cpu]) | ||
|
||
#include <asm-generic/topology.h> | ||
|
||
#endif /* _ASM_METAG_TOPOLOGY_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
/* | ||
* Meta cache partition manipulation. | ||
* | ||
* Copyright 2010 Imagination Technologies Ltd. | ||
*/ | ||
|
||
#include <linux/kernel.h> | ||
#include <linux/io.h> | ||
#include <linux/errno.h> | ||
#include <asm/processor.h> | ||
#include <asm/cachepart.h> | ||
#include <asm/metag_isa.h> | ||
#include <asm/metag_mem.h> | ||
|
||
#define SYSC_DCPART(n) (SYSC_DCPART0 + SYSC_xCPARTn_STRIDE * (n)) | ||
#define SYSC_ICPART(n) (SYSC_ICPART0 + SYSC_xCPARTn_STRIDE * (n)) | ||
|
||
#define CACHE_ASSOCIATIVITY 4 /* 4 way set-assosiative */ | ||
#define ICACHE 0 | ||
#define DCACHE 1 | ||
|
||
/* The CORE_CONFIG2 register is not available on Meta 1 */ | ||
#ifdef CONFIG_METAG_META21 | ||
unsigned int get_dcache_size(void) | ||
{ | ||
unsigned int config2 = metag_in32(METAC_CORE_CONFIG2); | ||
return 0x1000 << ((config2 & METAC_CORECFG2_DCSZ_BITS) | ||
>> METAC_CORECFG2_DCSZ_S); | ||
} | ||
|
||
unsigned int get_icache_size(void) | ||
{ | ||
unsigned int config2 = metag_in32(METAC_CORE_CONFIG2); | ||
return 0x1000 << ((config2 & METAC_CORE_C2ICSZ_BITS) | ||
>> METAC_CORE_C2ICSZ_S); | ||
} | ||
|
||
unsigned int get_global_dcache_size(void) | ||
{ | ||
unsigned int cpart = metag_in32(SYSC_DCPART(hard_processor_id())); | ||
unsigned int temp = cpart & SYSC_xCPARTG_AND_BITS; | ||
return (get_dcache_size() * ((temp >> SYSC_xCPARTG_AND_S) + 1)) >> 4; | ||
} | ||
|
||
unsigned int get_global_icache_size(void) | ||
{ | ||
unsigned int cpart = metag_in32(SYSC_ICPART(hard_processor_id())); | ||
unsigned int temp = cpart & SYSC_xCPARTG_AND_BITS; | ||
return (get_icache_size() * ((temp >> SYSC_xCPARTG_AND_S) + 1)) >> 4; | ||
} | ||
|
||
static unsigned int get_thread_cache_size(unsigned int cache, int thread_id) | ||
{ | ||
unsigned int cache_size; | ||
unsigned int t_cache_part; | ||
unsigned int isEnabled; | ||
unsigned int offset = 0; | ||
isEnabled = (cache == DCACHE ? metag_in32(MMCU_DCACHE_CTRL_ADDR) & 0x1 : | ||
metag_in32(MMCU_ICACHE_CTRL_ADDR) & 0x1); | ||
if (!isEnabled) | ||
return 0; | ||
#if PAGE_OFFSET >= LINGLOBAL_BASE | ||
/* Checking for global cache */ | ||
cache_size = (cache == DCACHE ? get_global_dache_size() : | ||
get_global_icache_size()); | ||
offset = 8; | ||
#else | ||
cache_size = (cache == DCACHE ? get_dcache_size() : | ||
get_icache_size()); | ||
#endif | ||
t_cache_part = (cache == DCACHE ? | ||
(metag_in32(SYSC_DCPART(thread_id)) >> offset) & 0xF : | ||
(metag_in32(SYSC_ICPART(thread_id)) >> offset) & 0xF); | ||
switch (t_cache_part) { | ||
case 0xF: | ||
return cache_size; | ||
case 0x7: | ||
return cache_size / 2; | ||
case 0x3: | ||
return cache_size / 4; | ||
case 0x1: | ||
return cache_size / 8; | ||
case 0: | ||
return cache_size / 16; | ||
} | ||
return -1; | ||
} | ||
|
||
void check_for_cache_aliasing(int thread_id) | ||
{ | ||
unsigned int thread_cache_size; | ||
unsigned int cache_type; | ||
for (cache_type = ICACHE; cache_type <= DCACHE; cache_type++) { | ||
thread_cache_size = | ||
get_thread_cache_size(cache_type, thread_id); | ||
if (thread_cache_size < 0) | ||
pr_emerg("Can't read %s cache size", \ | ||
cache_type ? "DCACHE" : "ICACHE"); | ||
else if (thread_cache_size == 0) | ||
/* Cache is off. No need to check for aliasing */ | ||
continue; | ||
if (thread_cache_size / CACHE_ASSOCIATIVITY > PAGE_SIZE) { | ||
pr_emerg("Cache aliasing detected in %s on Thread %d", | ||
cache_type ? "DCACHE" : "ICACHE", thread_id); | ||
pr_warn("Total %s size: %u bytes", | ||
cache_type ? "DCACHE" : "ICACHE ", | ||
cache_type ? get_dcache_size() | ||
: get_icache_size()); | ||
pr_warn("Thread %s size: %d bytes", | ||
cache_type ? "CACHE" : "ICACHE", | ||
thread_cache_size); | ||
pr_warn("Page Size: %lu bytes", PAGE_SIZE); | ||
} | ||
} | ||
} | ||
|
||
#else | ||
|
||
void check_for_cache_aliasing(int thread_id) | ||
{ | ||
return; | ||
} | ||
|
||
#endif |
Oops, something went wrong.