162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Coherency fabric: low level functions 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 Marvell 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Gregory CLEMENT <gregory.clement@free-electrons.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This file implements the assembly function to add a CPU to the 1062306a36Sopenharmony_ci * coherency fabric. This function is called by each of the secondary 1162306a36Sopenharmony_ci * CPUs during their early boot in an SMP kernel, this why this 1262306a36Sopenharmony_ci * function have to callable from assembly. It can also be called by a 1362306a36Sopenharmony_ci * primary CPU from C code during its boot. 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/linkage.h> 1762306a36Sopenharmony_ci#define ARMADA_XP_CFB_CTL_REG_OFFSET 0x0 1862306a36Sopenharmony_ci#define ARMADA_XP_CFB_CFG_REG_OFFSET 0x4 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <asm/assembler.h> 2162306a36Sopenharmony_ci#include <asm/cp15.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci .arch armv7-a 2462306a36Sopenharmony_ci .text 2562306a36Sopenharmony_ci/* 2662306a36Sopenharmony_ci * Returns the coherency base address in r1 (r0 is untouched), or 0 if 2762306a36Sopenharmony_ci * the coherency fabric is not enabled. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ciENTRY(ll_get_coherency_base) 3062306a36Sopenharmony_ci mrc p15, 0, r1, c1, c0, 0 3162306a36Sopenharmony_ci tst r1, #CR_M @ Check MMU bit enabled 3262306a36Sopenharmony_ci bne 1f 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci /* 3562306a36Sopenharmony_ci * MMU is disabled, use the physical address of the coherency 3662306a36Sopenharmony_ci * base address, (or 0x0 if the coherency fabric is not mapped) 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_ci adr r1, 3f 3962306a36Sopenharmony_ci ldr r3, [r1] 4062306a36Sopenharmony_ci ldr r1, [r1, r3] 4162306a36Sopenharmony_ci b 2f 4262306a36Sopenharmony_ci1: 4362306a36Sopenharmony_ci /* 4462306a36Sopenharmony_ci * MMU is enabled, use the virtual address of the coherency 4562306a36Sopenharmony_ci * base address. 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ci ldr r1, =coherency_base 4862306a36Sopenharmony_ci ldr r1, [r1] 4962306a36Sopenharmony_ci2: 5062306a36Sopenharmony_ci ret lr 5162306a36Sopenharmony_ciENDPROC(ll_get_coherency_base) 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* 5462306a36Sopenharmony_ci * Returns the coherency CPU mask in r3 (r0 is untouched). This 5562306a36Sopenharmony_ci * coherency CPU mask can be used with the coherency fabric 5662306a36Sopenharmony_ci * configuration and control registers. Note that the mask is already 5762306a36Sopenharmony_ci * endian-swapped as appropriate so that the calling functions do not 5862306a36Sopenharmony_ci * have to care about endianness issues while accessing the coherency 5962306a36Sopenharmony_ci * fabric registers 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_ciENTRY(ll_get_coherency_cpumask) 6262306a36Sopenharmony_ci mrc p15, 0, r3, cr0, cr0, 5 6362306a36Sopenharmony_ci and r3, r3, #15 6462306a36Sopenharmony_ci mov r2, #(1 << 24) 6562306a36Sopenharmony_ci lsl r3, r2, r3 6662306a36Sopenharmony_ciARM_BE8(rev r3, r3) 6762306a36Sopenharmony_ci ret lr 6862306a36Sopenharmony_ciENDPROC(ll_get_coherency_cpumask) 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* 7162306a36Sopenharmony_ci * ll_add_cpu_to_smp_group(), ll_enable_coherency() and 7262306a36Sopenharmony_ci * ll_disable_coherency() use the strex/ldrex instructions while the 7362306a36Sopenharmony_ci * MMU can be disabled. The Armada XP SoC has an exclusive monitor 7462306a36Sopenharmony_ci * that tracks transactions to Device and/or SO memory and thanks to 7562306a36Sopenharmony_ci * that, exclusive transactions are functional even when the MMU is 7662306a36Sopenharmony_ci * disabled. 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ciENTRY(ll_add_cpu_to_smp_group) 8062306a36Sopenharmony_ci /* 8162306a36Sopenharmony_ci * As r0 is not modified by ll_get_coherency_base() and 8262306a36Sopenharmony_ci * ll_get_coherency_cpumask(), we use it to temporarly save lr 8362306a36Sopenharmony_ci * and avoid it being modified by the branch and link 8462306a36Sopenharmony_ci * calls. This function is used very early in the secondary 8562306a36Sopenharmony_ci * CPU boot, and no stack is available at this point. 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci mov r0, lr 8862306a36Sopenharmony_ci bl ll_get_coherency_base 8962306a36Sopenharmony_ci /* Bail out if the coherency is not enabled */ 9062306a36Sopenharmony_ci cmp r1, #0 9162306a36Sopenharmony_ci reteq r0 9262306a36Sopenharmony_ci bl ll_get_coherency_cpumask 9362306a36Sopenharmony_ci mov lr, r0 9462306a36Sopenharmony_ci add r0, r1, #ARMADA_XP_CFB_CFG_REG_OFFSET 9562306a36Sopenharmony_ci1: 9662306a36Sopenharmony_ci ldrex r2, [r0] 9762306a36Sopenharmony_ci orr r2, r2, r3 9862306a36Sopenharmony_ci strex r1, r2, [r0] 9962306a36Sopenharmony_ci cmp r1, #0 10062306a36Sopenharmony_ci bne 1b 10162306a36Sopenharmony_ci ret lr 10262306a36Sopenharmony_ciENDPROC(ll_add_cpu_to_smp_group) 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ciENTRY(ll_enable_coherency) 10562306a36Sopenharmony_ci /* 10662306a36Sopenharmony_ci * As r0 is not modified by ll_get_coherency_base() and 10762306a36Sopenharmony_ci * ll_get_coherency_cpumask(), we use it to temporarly save lr 10862306a36Sopenharmony_ci * and avoid it being modified by the branch and link 10962306a36Sopenharmony_ci * calls. This function is used very early in the secondary 11062306a36Sopenharmony_ci * CPU boot, and no stack is available at this point. 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_ci mov r0, lr 11362306a36Sopenharmony_ci bl ll_get_coherency_base 11462306a36Sopenharmony_ci /* Bail out if the coherency is not enabled */ 11562306a36Sopenharmony_ci cmp r1, #0 11662306a36Sopenharmony_ci reteq r0 11762306a36Sopenharmony_ci bl ll_get_coherency_cpumask 11862306a36Sopenharmony_ci mov lr, r0 11962306a36Sopenharmony_ci add r0, r1, #ARMADA_XP_CFB_CTL_REG_OFFSET 12062306a36Sopenharmony_ci1: 12162306a36Sopenharmony_ci ldrex r2, [r0] 12262306a36Sopenharmony_ci orr r2, r2, r3 12362306a36Sopenharmony_ci strex r1, r2, [r0] 12462306a36Sopenharmony_ci cmp r1, #0 12562306a36Sopenharmony_ci bne 1b 12662306a36Sopenharmony_ci dsb 12762306a36Sopenharmony_ci mov r0, #0 12862306a36Sopenharmony_ci ret lr 12962306a36Sopenharmony_ciENDPROC(ll_enable_coherency) 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ciENTRY(ll_disable_coherency) 13262306a36Sopenharmony_ci /* 13362306a36Sopenharmony_ci * As r0 is not modified by ll_get_coherency_base() and 13462306a36Sopenharmony_ci * ll_get_coherency_cpumask(), we use it to temporarly save lr 13562306a36Sopenharmony_ci * and avoid it being modified by the branch and link 13662306a36Sopenharmony_ci * calls. This function is used very early in the secondary 13762306a36Sopenharmony_ci * CPU boot, and no stack is available at this point. 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_ci mov r0, lr 14062306a36Sopenharmony_ci bl ll_get_coherency_base 14162306a36Sopenharmony_ci /* Bail out if the coherency is not enabled */ 14262306a36Sopenharmony_ci cmp r1, #0 14362306a36Sopenharmony_ci reteq r0 14462306a36Sopenharmony_ci bl ll_get_coherency_cpumask 14562306a36Sopenharmony_ci mov lr, r0 14662306a36Sopenharmony_ci add r0, r1, #ARMADA_XP_CFB_CTL_REG_OFFSET 14762306a36Sopenharmony_ci1: 14862306a36Sopenharmony_ci ldrex r2, [r0] 14962306a36Sopenharmony_ci bic r2, r2, r3 15062306a36Sopenharmony_ci strex r1, r2, [r0] 15162306a36Sopenharmony_ci cmp r1, #0 15262306a36Sopenharmony_ci bne 1b 15362306a36Sopenharmony_ci dsb 15462306a36Sopenharmony_ci ret lr 15562306a36Sopenharmony_ciENDPROC(ll_disable_coherency) 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci .align 2 15862306a36Sopenharmony_ci3: 15962306a36Sopenharmony_ci .long coherency_phys_base - . 160