ARC changes for 3.14
* IPI optimization and cleanups * Support for bootloader provided external Device Tree blobs -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.14 (GNU/Linux) iQIcBAABAgAGBQJS4mwzAAoJEGnX8d3iisJeVuEP/AhADnG9Gtq/RmvJTAydpvQI lDLgkK2rZTBzJ3q57R1pZVji0wg3z1kgQN5Eg7u3UvqcRuZ+pPGLY4JxlNI3o0He +8rtc5V8lbMS+StwjhOVsuNRc7GNKrUIx8Xy3IBQkadwheqmOF6XV5sgxfpRMdxf vP/imsuSx2bkgt9GAgGyggf+qmKL1/XYFbZVdpHf60Jr6rR1YFGfDJj5tjDIOGXH hbHkppk2806XK29MDzRZ0zXGuDrhFJm9vRdaT/DIwkeKb2tfc2JoibrpxeO/81pp BvApZQG8RgCK70D8nYKMK0oiwtFZ6V0MVh8/POISbxuGz8cWbFjM2ylaNCyIsYcb L6KUs0gKkf5J5PTalN4qIVCbuYFc7fG8aoCHdRm7lL5f0EmjoUgweeTs8D1BUQxc Gpt/TEIAWGPTj1eULdFRyjpRNiw9NpAywGjgsZ2Uz9cpH8W37vfAA01UDgKvqo3E kiff94g8RX5zbYJBtDy0HFqno+YGSNEUkYQTUCDsHOLS5sMRrgjF+zibtDORYobe s8RSYLCuNoeRLpkeZv5Kjeum8RY8HbYQM0k+bhS0nEQqGxFstC5PXp4fSkFaveWg 8+30kTtjtChDtgD7C9Ql92xWOPCo0sGoTrQoPRkEWSTgFrk8TVDi0hn0iwMG2NTn 20IxJmH/cjXPR2ELe0QL =+tHd -----END PGP SIGNATURE----- Merge tag 'arc-v3.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vgupta/arc Pull ARC changes from Vineet Gupta: - IPI optimization and cleanups - Support for bootloader provided external Device Tree blobs * tag 'arc-v3.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vgupta/arc: ARC: [cmdline] support External Device Trees from u-boot ARC: [cmdline] uboot cmdline handling rework ARC: [SMP] optimize IPI send and receive ARC: [SMP] simplify IPI code ARC: [SMP] cpu halt interface doesn't need "self" cpu-id ARC: [SMP] IPI ACK interface doesn't need "self" cpu-id ARC: [SMP] cpumask not needed in IPI send path
This commit is contained in:
commit
7ae0e06b90
|
@ -409,17 +409,6 @@ config ARC_DBG_TLB_MISS_COUNT
|
||||||
Counts number of I and D TLB Misses and exports them via Debugfs
|
Counts number of I and D TLB Misses and exports them via Debugfs
|
||||||
The counters can be cleared via Debugfs as well
|
The counters can be cleared via Debugfs as well
|
||||||
|
|
||||||
config CMDLINE_UBOOT
|
|
||||||
bool "Support U-boot kernel command line passing"
|
|
||||||
default n
|
|
||||||
help
|
|
||||||
If you are using U-boot (www.denx.de) and wish to pass the kernel
|
|
||||||
command line from the U-boot environment to the Linux kernel then
|
|
||||||
switch this option on.
|
|
||||||
ARC U-boot will setup the cmdline in RAM/flash and set r2 to point
|
|
||||||
to it. kernel startup code will append this to DeviceTree
|
|
||||||
/bootargs provided cmdline args.
|
|
||||||
|
|
||||||
config ARC_BUILTIN_DTB_NAME
|
config ARC_BUILTIN_DTB_NAME
|
||||||
string "Built in DTB"
|
string "Built in DTB"
|
||||||
help
|
help
|
||||||
|
|
|
@ -46,14 +46,14 @@ extern int smp_ipi_irq_setup(int cpu, int irq);
|
||||||
*
|
*
|
||||||
* @info: SoC SMP specific info for /proc/cpuinfo etc
|
* @info: SoC SMP specific info for /proc/cpuinfo etc
|
||||||
* @cpu_kick: For Master to kickstart a cpu (optionally at a PC)
|
* @cpu_kick: For Master to kickstart a cpu (optionally at a PC)
|
||||||
* @ipi_send: To send IPI to a @cpumask
|
* @ipi_send: To send IPI to a @cpu
|
||||||
* @ips_clear: To clear IPI received by @cpu at @irq
|
* @ips_clear: To clear IPI received at @irq
|
||||||
*/
|
*/
|
||||||
struct plat_smp_ops {
|
struct plat_smp_ops {
|
||||||
const char *info;
|
const char *info;
|
||||||
void (*cpu_kick)(int cpu, unsigned long pc);
|
void (*cpu_kick)(int cpu, unsigned long pc);
|
||||||
void (*ipi_send)(void *callmap);
|
void (*ipi_send)(int cpu);
|
||||||
void (*ipi_clear)(int cpu, int irq);
|
void (*ipi_clear)(int irq);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* TBD: stop exporting it for direct population by platform */
|
/* TBD: stop exporting it for direct population by platform */
|
||||||
|
|
|
@ -49,25 +49,13 @@ stext:
|
||||||
st.ab 0, [r5,4]
|
st.ab 0, [r5,4]
|
||||||
brlt r5, r6, 1b
|
brlt r5, r6, 1b
|
||||||
|
|
||||||
#ifdef CONFIG_CMDLINE_UBOOT
|
; Uboot - kernel ABI
|
||||||
; support for bootloader provided cmdline
|
; r0 = [0] No uboot interaction, [1] cmdline in r2, [2] DTB in r2
|
||||||
; If cmdline passed by u-boot, then
|
; r1 = magic number (board identity, unused as of now
|
||||||
; r0 = 1 (because ATAGS parsing, now retired, used to use 0)
|
; r2 = pointer to uboot provided cmdline or external DTB in mem
|
||||||
; r1 = magic number (board identity)
|
; These are handled later in setup_arch()
|
||||||
; r2 = addr of cmdline string (somewhere in memory/flash)
|
st r0, [@uboot_tag]
|
||||||
|
st r2, [@uboot_arg]
|
||||||
brne r0, 1, .Lother_bootup_chores ; u-boot didn't pass cmdline
|
|
||||||
breq r2, 0, .Lother_bootup_chores ; or cmdline is NULL
|
|
||||||
|
|
||||||
mov r5, @command_line
|
|
||||||
1:
|
|
||||||
ldb.ab r6, [r2, 1]
|
|
||||||
breq r6, 0, .Lother_bootup_chores
|
|
||||||
b.d 1b
|
|
||||||
stb.ab r6, [r5, 1]
|
|
||||||
#endif
|
|
||||||
|
|
||||||
.Lother_bootup_chores:
|
|
||||||
|
|
||||||
; Identify if running on ISS vs Silicon
|
; Identify if running on ISS vs Silicon
|
||||||
; IDENTITY Reg [ 3 2 1 0 ]
|
; IDENTITY Reg [ 3 2 1 0 ]
|
||||||
|
|
|
@ -29,7 +29,10 @@
|
||||||
|
|
||||||
int running_on_hw = 1; /* vs. on ISS */
|
int running_on_hw = 1; /* vs. on ISS */
|
||||||
|
|
||||||
char __initdata command_line[COMMAND_LINE_SIZE];
|
/* Part of U-boot ABI: see head.S */
|
||||||
|
int __initdata uboot_tag;
|
||||||
|
char __initdata *uboot_arg;
|
||||||
|
|
||||||
const struct machine_desc *machine_desc;
|
const struct machine_desc *machine_desc;
|
||||||
|
|
||||||
struct task_struct *_current_task[NR_CPUS]; /* For stack switching */
|
struct task_struct *_current_task[NR_CPUS]; /* For stack switching */
|
||||||
|
@ -311,19 +314,40 @@ void setup_processor(void)
|
||||||
arc_chk_fpu();
|
arc_chk_fpu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int is_kernel(unsigned long addr)
|
||||||
|
{
|
||||||
|
if (addr >= (unsigned long)_stext && addr <= (unsigned long)_end)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void __init setup_arch(char **cmdline_p)
|
void __init setup_arch(char **cmdline_p)
|
||||||
{
|
{
|
||||||
/* This also populates @boot_command_line from /bootargs */
|
/* make sure that uboot passed pointer to cmdline/dtb is valid */
|
||||||
machine_desc = setup_machine_fdt(__dtb_start);
|
if (uboot_tag && is_kernel((unsigned long)uboot_arg))
|
||||||
if (!machine_desc)
|
panic("Invalid uboot arg\n");
|
||||||
panic("Embedded DT invalid\n");
|
|
||||||
|
|
||||||
/* Append any u-boot provided cmdline */
|
/* See if u-boot passed an external Device Tree blob */
|
||||||
#ifdef CONFIG_CMDLINE_UBOOT
|
machine_desc = setup_machine_fdt(uboot_arg); /* uboot_tag == 2 */
|
||||||
/* Add a whitespace seperator between the 2 cmdlines */
|
if (!machine_desc) {
|
||||||
strlcat(boot_command_line, " ", COMMAND_LINE_SIZE);
|
/* No, so try the embedded one */
|
||||||
strlcat(boot_command_line, command_line, COMMAND_LINE_SIZE);
|
machine_desc = setup_machine_fdt(__dtb_start);
|
||||||
#endif
|
if (!machine_desc)
|
||||||
|
panic("Embedded DT invalid\n");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we are here, it is established that @uboot_arg didn't
|
||||||
|
* point to DT blob. Instead if u-boot says it is cmdline,
|
||||||
|
* Appent to embedded DT cmdline.
|
||||||
|
* setup_machine_fdt() would have populated @boot_command_line
|
||||||
|
*/
|
||||||
|
if (uboot_tag == 1) {
|
||||||
|
/* Ensure a whitespace between the 2 cmdlines */
|
||||||
|
strlcat(boot_command_line, " ", COMMAND_LINE_SIZE);
|
||||||
|
strlcat(boot_command_line, uboot_arg,
|
||||||
|
COMMAND_LINE_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Save unparsed command line copy for /proc/cmdline */
|
/* Save unparsed command line copy for /proc/cmdline */
|
||||||
*cmdline_p = boot_command_line;
|
*cmdline_p = boot_command_line;
|
||||||
|
|
|
@ -197,51 +197,65 @@ int __init setup_profiling_timer(unsigned int multiplier)
|
||||||
/* Inter Processor Interrupt Handling */
|
/* Inter Processor Interrupt Handling */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
/*
|
|
||||||
* structures for inter-processor calls
|
|
||||||
* A Collection of single bit ipi messages
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TODO_rajesh investigate tlb message types.
|
|
||||||
* IPI Timer not needed because each ARC has an individual Interrupting Timer
|
|
||||||
*/
|
|
||||||
enum ipi_msg_type {
|
enum ipi_msg_type {
|
||||||
IPI_NOP = 0,
|
IPI_EMPTY = 0,
|
||||||
IPI_RESCHEDULE = 1,
|
IPI_RESCHEDULE = 1,
|
||||||
IPI_CALL_FUNC,
|
IPI_CALL_FUNC,
|
||||||
IPI_CPU_STOP
|
IPI_CPU_STOP,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ipi_data {
|
/*
|
||||||
unsigned long bits;
|
* In arches with IRQ for each msg type (above), receiver can use IRQ-id to
|
||||||
};
|
* figure out what msg was sent. For those which don't (ARC has dedicated IPI
|
||||||
|
* IRQ), the msg-type needs to be conveyed via per-cpu data
|
||||||
|
*/
|
||||||
|
|
||||||
static DEFINE_PER_CPU(struct ipi_data, ipi_data);
|
static DEFINE_PER_CPU(unsigned long, ipi_data);
|
||||||
|
|
||||||
static void ipi_send_msg(const struct cpumask *callmap, enum ipi_msg_type msg)
|
static void ipi_send_msg_one(int cpu, enum ipi_msg_type msg)
|
||||||
{
|
{
|
||||||
|
unsigned long __percpu *ipi_data_ptr = per_cpu_ptr(&ipi_data, cpu);
|
||||||
|
unsigned long old, new;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned int cpu;
|
|
||||||
|
pr_debug("%d Sending msg [%d] to %d\n", smp_processor_id(), msg, cpu);
|
||||||
|
|
||||||
local_irq_save(flags);
|
local_irq_save(flags);
|
||||||
|
|
||||||
for_each_cpu(cpu, callmap) {
|
/*
|
||||||
struct ipi_data *ipi = &per_cpu(ipi_data, cpu);
|
* Atomically write new msg bit (in case others are writing too),
|
||||||
set_bit(msg, &ipi->bits);
|
* and read back old value
|
||||||
}
|
*/
|
||||||
|
do {
|
||||||
|
new = old = *ipi_data_ptr;
|
||||||
|
new |= 1U << msg;
|
||||||
|
} while (cmpxchg(ipi_data_ptr, old, new) != old);
|
||||||
|
|
||||||
/* Call the platform specific cross-CPU call function */
|
/*
|
||||||
if (plat_smp_ops.ipi_send)
|
* Call the platform specific IPI kick function, but avoid if possible:
|
||||||
plat_smp_ops.ipi_send((void *)callmap);
|
* Only do so if there's no pending msg from other concurrent sender(s).
|
||||||
|
* Otherwise, recevier will see this msg as well when it takes the
|
||||||
|
* IPI corresponding to that msg. This is true, even if it is already in
|
||||||
|
* IPI handler, because !@old means it has not yet dequeued the msg(s)
|
||||||
|
* so @new msg can be a free-loader
|
||||||
|
*/
|
||||||
|
if (plat_smp_ops.ipi_send && !old)
|
||||||
|
plat_smp_ops.ipi_send(cpu);
|
||||||
|
|
||||||
local_irq_restore(flags);
|
local_irq_restore(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ipi_send_msg(const struct cpumask *callmap, enum ipi_msg_type msg)
|
||||||
|
{
|
||||||
|
unsigned int cpu;
|
||||||
|
|
||||||
|
for_each_cpu(cpu, callmap)
|
||||||
|
ipi_send_msg_one(cpu, msg);
|
||||||
|
}
|
||||||
|
|
||||||
void smp_send_reschedule(int cpu)
|
void smp_send_reschedule(int cpu)
|
||||||
{
|
{
|
||||||
ipi_send_msg(cpumask_of(cpu), IPI_RESCHEDULE);
|
ipi_send_msg_one(cpu, IPI_RESCHEDULE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void smp_send_stop(void)
|
void smp_send_stop(void)
|
||||||
|
@ -254,7 +268,7 @@ void smp_send_stop(void)
|
||||||
|
|
||||||
void arch_send_call_function_single_ipi(int cpu)
|
void arch_send_call_function_single_ipi(int cpu)
|
||||||
{
|
{
|
||||||
ipi_send_msg(cpumask_of(cpu), IPI_CALL_FUNC);
|
ipi_send_msg_one(cpu, IPI_CALL_FUNC);
|
||||||
}
|
}
|
||||||
|
|
||||||
void arch_send_call_function_ipi_mask(const struct cpumask *mask)
|
void arch_send_call_function_ipi_mask(const struct cpumask *mask)
|
||||||
|
@ -265,33 +279,29 @@ void arch_send_call_function_ipi_mask(const struct cpumask *mask)
|
||||||
/*
|
/*
|
||||||
* ipi_cpu_stop - handle IPI from smp_send_stop()
|
* ipi_cpu_stop - handle IPI from smp_send_stop()
|
||||||
*/
|
*/
|
||||||
static void ipi_cpu_stop(unsigned int cpu)
|
static void ipi_cpu_stop(void)
|
||||||
{
|
{
|
||||||
machine_halt();
|
machine_halt();
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void __do_IPI(unsigned long *ops, struct ipi_data *ipi, int cpu)
|
static inline void __do_IPI(unsigned long msg)
|
||||||
{
|
{
|
||||||
unsigned long msg = 0;
|
switch (msg) {
|
||||||
|
case IPI_RESCHEDULE:
|
||||||
|
scheduler_ipi();
|
||||||
|
break;
|
||||||
|
|
||||||
do {
|
case IPI_CALL_FUNC:
|
||||||
msg = find_next_bit(ops, BITS_PER_LONG, msg+1);
|
generic_smp_call_function_interrupt();
|
||||||
|
break;
|
||||||
|
|
||||||
switch (msg) {
|
case IPI_CPU_STOP:
|
||||||
case IPI_RESCHEDULE:
|
ipi_cpu_stop();
|
||||||
scheduler_ipi();
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
case IPI_CALL_FUNC:
|
|
||||||
generic_smp_call_function_interrupt();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IPI_CPU_STOP:
|
|
||||||
ipi_cpu_stop(cpu);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while (msg < BITS_PER_LONG);
|
|
||||||
|
|
||||||
|
default:
|
||||||
|
pr_warn("IPI with unexpected msg %ld\n", msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -300,19 +310,25 @@ static inline void __do_IPI(unsigned long *ops, struct ipi_data *ipi, int cpu)
|
||||||
*/
|
*/
|
||||||
irqreturn_t do_IPI(int irq, void *dev_id)
|
irqreturn_t do_IPI(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
int cpu = smp_processor_id();
|
unsigned long pending;
|
||||||
struct ipi_data *ipi = &per_cpu(ipi_data, cpu);
|
|
||||||
unsigned long ops;
|
pr_debug("IPI [%ld] received on cpu %d\n",
|
||||||
|
*this_cpu_ptr(&ipi_data), smp_processor_id());
|
||||||
|
|
||||||
if (plat_smp_ops.ipi_clear)
|
if (plat_smp_ops.ipi_clear)
|
||||||
plat_smp_ops.ipi_clear(cpu, irq);
|
plat_smp_ops.ipi_clear(irq);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* XXX: is this loop really needed
|
* "dequeue" the msg corresponding to this IPI (and possibly other
|
||||||
* And do we need to move ipi_clean inside
|
* piggybacked msg from elided IPIs: see ipi_send_msg_one() above)
|
||||||
*/
|
*/
|
||||||
while ((ops = xchg(&ipi->bits, 0)) != 0)
|
pending = xchg(this_cpu_ptr(&ipi_data), 0);
|
||||||
__do_IPI(&ops, ipi, cpu);
|
|
||||||
|
do {
|
||||||
|
unsigned long msg = __ffs(pending);
|
||||||
|
__do_IPI(msg);
|
||||||
|
pending &= ~(1U << msg);
|
||||||
|
} while (pending);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,18 +88,14 @@ void iss_model_init_smp(unsigned int cpu)
|
||||||
smp_ipi_irq_setup(cpu, IDU_INTERRUPT_0 + cpu);
|
smp_ipi_irq_setup(cpu, IDU_INTERRUPT_0 + cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void iss_model_ipi_send(void *arg)
|
static void iss_model_ipi_send(int cpu)
|
||||||
{
|
{
|
||||||
struct cpumask *callmap = arg;
|
idu_irq_assert(cpu);
|
||||||
unsigned int cpu;
|
|
||||||
|
|
||||||
for_each_cpu(cpu, callmap)
|
|
||||||
idu_irq_assert(cpu);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void iss_model_ipi_clear(int cpu, int irq)
|
static void iss_model_ipi_clear(int irq)
|
||||||
{
|
{
|
||||||
idu_irq_clear(IDU_INTERRUPT_0 + cpu);
|
idu_irq_clear(IDU_INTERRUPT_0 + smp_processor_id());
|
||||||
}
|
}
|
||||||
|
|
||||||
void iss_model_init_early_smp(void)
|
void iss_model_init_early_smp(void)
|
||||||
|
|
Loading…
Reference in a new issue