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

Commit 42682c6c authored by James Hogan's avatar James Hogan
Browse files

metag: SMP support



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: default avatarJames Hogan <james.hogan@imgtec.com>
parent fdabf525
Loading
Loading
Loading
Loading
+42 −0
Original line number Diff line number Diff line
/*
 * 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
+35 −0
Original line number Diff line number Diff line
#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
+29 −0
Original line number Diff line number Diff line
#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 */
+53 −0
Original line number Diff line number Diff line
#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 */
+124 −0
Original line number Diff line number Diff line
/*
 * 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
Loading