1ed69bdd8SLorenzo Pieralisi /* 2ed69bdd8SLorenzo Pieralisi * CCI cache coherent interconnect driver 3ed69bdd8SLorenzo Pieralisi * 4ed69bdd8SLorenzo Pieralisi * Copyright (C) 2013 ARM Ltd. 5ed69bdd8SLorenzo Pieralisi * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> 6ed69bdd8SLorenzo Pieralisi * 7ed69bdd8SLorenzo Pieralisi * This program is free software; you can redistribute it and/or modify 8ed69bdd8SLorenzo Pieralisi * it under the terms of the GNU General Public License version 2 as 9ed69bdd8SLorenzo Pieralisi * published by the Free Software Foundation. 10ed69bdd8SLorenzo Pieralisi * 11ed69bdd8SLorenzo Pieralisi * This program is distributed "as is" WITHOUT ANY WARRANTY of any 12ed69bdd8SLorenzo Pieralisi * kind, whether express or implied; without even the implied warranty 13ed69bdd8SLorenzo Pieralisi * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14ed69bdd8SLorenzo Pieralisi * GNU General Public License for more details. 15ed69bdd8SLorenzo Pieralisi */ 16ed69bdd8SLorenzo Pieralisi 17ed69bdd8SLorenzo Pieralisi #include <linux/arm-cci.h> 18ed69bdd8SLorenzo Pieralisi #include <linux/io.h> 19ed69bdd8SLorenzo Pieralisi #include <linux/module.h> 20ed69bdd8SLorenzo Pieralisi #include <linux/of_address.h> 21b91c8f28SPunit Agrawal #include <linux/of_platform.h> 22b91c8f28SPunit Agrawal #include <linux/platform_device.h> 23ed69bdd8SLorenzo Pieralisi #include <linux/slab.h> 24ed69bdd8SLorenzo Pieralisi 25ed69bdd8SLorenzo Pieralisi #include <asm/cacheflush.h> 26ed69bdd8SLorenzo Pieralisi #include <asm/smp_plat.h> 27ed69bdd8SLorenzo Pieralisi 283de6be7aSRobin Murphy /* Referenced read-only by the PMU driver; see drivers/perf/arm-cci.c */ 293de6be7aSRobin Murphy void __iomem *cci_ctrl_base; 30f6b9e83cSSuzuki K. Poulose static unsigned long cci_ctrl_phys; 31ed69bdd8SLorenzo Pieralisi 32ee8e5d5fSSuzuki K. Poulose #ifdef CONFIG_ARM_CCI400_PORT_CTRL 33ed69bdd8SLorenzo Pieralisi struct cci_nb_ports { 34ed69bdd8SLorenzo Pieralisi unsigned int nb_ace; 35ed69bdd8SLorenzo Pieralisi unsigned int nb_ace_lite; 36ed69bdd8SLorenzo Pieralisi }; 37ed69bdd8SLorenzo Pieralisi 38f6b9e83cSSuzuki K. Poulose static const struct cci_nb_ports cci400_ports = { 39f6b9e83cSSuzuki K. Poulose .nb_ace = 2, 40f6b9e83cSSuzuki K. Poulose .nb_ace_lite = 3 41ed69bdd8SLorenzo Pieralisi }; 42ed69bdd8SLorenzo Pieralisi 43ee8e5d5fSSuzuki K. Poulose #define CCI400_PORTS_DATA (&cci400_ports) 44ee8e5d5fSSuzuki K. Poulose #else 45ee8e5d5fSSuzuki K. Poulose #define CCI400_PORTS_DATA (NULL) 46ee8e5d5fSSuzuki K. Poulose #endif 47ee8e5d5fSSuzuki K. Poulose 48f6b9e83cSSuzuki K. Poulose static const struct of_device_id arm_cci_matches[] = { 49ee8e5d5fSSuzuki K. Poulose #ifdef CONFIG_ARM_CCI400_COMMON 50ee8e5d5fSSuzuki K. Poulose {.compatible = "arm,cci-400", .data = CCI400_PORTS_DATA }, 51ee8e5d5fSSuzuki K. Poulose #endif 523d2e8701SSuzuki K Poulose #ifdef CONFIG_ARM_CCI5xx_PMU 53a95791efSSuzuki K. Poulose { .compatible = "arm,cci-500", }, 54d7dd5fd7SSuzuki K Poulose { .compatible = "arm,cci-550", }, 55a95791efSSuzuki K. Poulose #endif 56f6b9e83cSSuzuki K. Poulose {}, 57ed69bdd8SLorenzo Pieralisi }; 58ed69bdd8SLorenzo Pieralisi 59f4d58938SSuzuki K. Poulose #define DRIVER_NAME "ARM-CCI" 60b91c8f28SPunit Agrawal 61b91c8f28SPunit Agrawal static int cci_platform_probe(struct platform_device *pdev) 62b91c8f28SPunit Agrawal { 63b91c8f28SPunit Agrawal if (!cci_probed()) 64b91c8f28SPunit Agrawal return -ENODEV; 65b91c8f28SPunit Agrawal 66b91c8f28SPunit Agrawal return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); 67b91c8f28SPunit Agrawal } 68b91c8f28SPunit Agrawal 69f6b9e83cSSuzuki K. Poulose static struct platform_driver cci_platform_driver = { 70f6b9e83cSSuzuki K. Poulose .driver = { 71f6b9e83cSSuzuki K. Poulose .name = DRIVER_NAME, 72f6b9e83cSSuzuki K. Poulose .of_match_table = arm_cci_matches, 73f6b9e83cSSuzuki K. Poulose }, 74f6b9e83cSSuzuki K. Poulose .probe = cci_platform_probe, 75f6b9e83cSSuzuki K. Poulose }; 76f6b9e83cSSuzuki K. Poulose 77f6b9e83cSSuzuki K. Poulose static int __init cci_platform_init(void) 78f6b9e83cSSuzuki K. Poulose { 79f6b9e83cSSuzuki K. Poulose return platform_driver_register(&cci_platform_driver); 80f6b9e83cSSuzuki K. Poulose } 81f6b9e83cSSuzuki K. Poulose 82ee8e5d5fSSuzuki K. Poulose #ifdef CONFIG_ARM_CCI400_PORT_CTRL 83b91c8f28SPunit Agrawal 84f6b9e83cSSuzuki K. Poulose #define CCI_PORT_CTRL 0x0 85f6b9e83cSSuzuki K. Poulose #define CCI_CTRL_STATUS 0xc 86f6b9e83cSSuzuki K. Poulose 87f6b9e83cSSuzuki K. Poulose #define CCI_ENABLE_SNOOP_REQ 0x1 88f6b9e83cSSuzuki K. Poulose #define CCI_ENABLE_DVM_REQ 0x2 89f6b9e83cSSuzuki K. Poulose #define CCI_ENABLE_REQ (CCI_ENABLE_SNOOP_REQ | CCI_ENABLE_DVM_REQ) 90f6b9e83cSSuzuki K. Poulose 91f6b9e83cSSuzuki K. Poulose enum cci_ace_port_type { 92f6b9e83cSSuzuki K. Poulose ACE_INVALID_PORT = 0x0, 93f6b9e83cSSuzuki K. Poulose ACE_PORT, 94f6b9e83cSSuzuki K. Poulose ACE_LITE_PORT, 95f6b9e83cSSuzuki K. Poulose }; 96f6b9e83cSSuzuki K. Poulose 97f6b9e83cSSuzuki K. Poulose struct cci_ace_port { 98f6b9e83cSSuzuki K. Poulose void __iomem *base; 99f6b9e83cSSuzuki K. Poulose unsigned long phys; 100f6b9e83cSSuzuki K. Poulose enum cci_ace_port_type type; 101f6b9e83cSSuzuki K. Poulose struct device_node *dn; 102f6b9e83cSSuzuki K. Poulose }; 103f6b9e83cSSuzuki K. Poulose 104f6b9e83cSSuzuki K. Poulose static struct cci_ace_port *ports; 105f6b9e83cSSuzuki K. Poulose static unsigned int nb_cci_ports; 106f6b9e83cSSuzuki K. Poulose 107ed69bdd8SLorenzo Pieralisi struct cpu_port { 108ed69bdd8SLorenzo Pieralisi u64 mpidr; 109ed69bdd8SLorenzo Pieralisi u32 port; 110ed69bdd8SLorenzo Pieralisi }; 11162158f81SNicolas Pitre 112ed69bdd8SLorenzo Pieralisi /* 113ed69bdd8SLorenzo Pieralisi * Use the port MSB as valid flag, shift can be made dynamic 114ed69bdd8SLorenzo Pieralisi * by computing number of bits required for port indexes. 115ed69bdd8SLorenzo Pieralisi * Code disabling CCI cpu ports runs with D-cache invalidated 116ed69bdd8SLorenzo Pieralisi * and SCTLR bit clear so data accesses must be kept to a minimum 117ed69bdd8SLorenzo Pieralisi * to improve performance; for now shift is left static to 118ed69bdd8SLorenzo Pieralisi * avoid one more data access while disabling the CCI port. 119ed69bdd8SLorenzo Pieralisi */ 120ed69bdd8SLorenzo Pieralisi #define PORT_VALID_SHIFT 31 121ed69bdd8SLorenzo Pieralisi #define PORT_VALID (0x1 << PORT_VALID_SHIFT) 122ed69bdd8SLorenzo Pieralisi 123ed69bdd8SLorenzo Pieralisi static inline void init_cpu_port(struct cpu_port *port, u32 index, u64 mpidr) 124ed69bdd8SLorenzo Pieralisi { 125ed69bdd8SLorenzo Pieralisi port->port = PORT_VALID | index; 126ed69bdd8SLorenzo Pieralisi port->mpidr = mpidr; 127ed69bdd8SLorenzo Pieralisi } 128ed69bdd8SLorenzo Pieralisi 129ed69bdd8SLorenzo Pieralisi static inline bool cpu_port_is_valid(struct cpu_port *port) 130ed69bdd8SLorenzo Pieralisi { 131ed69bdd8SLorenzo Pieralisi return !!(port->port & PORT_VALID); 132ed69bdd8SLorenzo Pieralisi } 133ed69bdd8SLorenzo Pieralisi 134ed69bdd8SLorenzo Pieralisi static inline bool cpu_port_match(struct cpu_port *port, u64 mpidr) 135ed69bdd8SLorenzo Pieralisi { 136ed69bdd8SLorenzo Pieralisi return port->mpidr == (mpidr & MPIDR_HWID_BITMASK); 137ed69bdd8SLorenzo Pieralisi } 138ed69bdd8SLorenzo Pieralisi 139ed69bdd8SLorenzo Pieralisi static struct cpu_port cpu_port[NR_CPUS]; 140ed69bdd8SLorenzo Pieralisi 141ed69bdd8SLorenzo Pieralisi /** 142ed69bdd8SLorenzo Pieralisi * __cci_ace_get_port - Function to retrieve the port index connected to 143ed69bdd8SLorenzo Pieralisi * a cpu or device. 144ed69bdd8SLorenzo Pieralisi * 145ed69bdd8SLorenzo Pieralisi * @dn: device node of the device to look-up 146ed69bdd8SLorenzo Pieralisi * @type: port type 147ed69bdd8SLorenzo Pieralisi * 148ed69bdd8SLorenzo Pieralisi * Return value: 149ed69bdd8SLorenzo Pieralisi * - CCI port index if success 150ed69bdd8SLorenzo Pieralisi * - -ENODEV if failure 151ed69bdd8SLorenzo Pieralisi */ 152ed69bdd8SLorenzo Pieralisi static int __cci_ace_get_port(struct device_node *dn, int type) 153ed69bdd8SLorenzo Pieralisi { 154ed69bdd8SLorenzo Pieralisi int i; 155ed69bdd8SLorenzo Pieralisi bool ace_match; 156ed69bdd8SLorenzo Pieralisi struct device_node *cci_portn; 157ed69bdd8SLorenzo Pieralisi 158ed69bdd8SLorenzo Pieralisi cci_portn = of_parse_phandle(dn, "cci-control-port", 0); 159ed69bdd8SLorenzo Pieralisi for (i = 0; i < nb_cci_ports; i++) { 160ed69bdd8SLorenzo Pieralisi ace_match = ports[i].type == type; 161ed69bdd8SLorenzo Pieralisi if (ace_match && cci_portn == ports[i].dn) 162ed69bdd8SLorenzo Pieralisi return i; 163ed69bdd8SLorenzo Pieralisi } 164ed69bdd8SLorenzo Pieralisi return -ENODEV; 165ed69bdd8SLorenzo Pieralisi } 166ed69bdd8SLorenzo Pieralisi 167ed69bdd8SLorenzo Pieralisi int cci_ace_get_port(struct device_node *dn) 168ed69bdd8SLorenzo Pieralisi { 169ed69bdd8SLorenzo Pieralisi return __cci_ace_get_port(dn, ACE_LITE_PORT); 170ed69bdd8SLorenzo Pieralisi } 171ed69bdd8SLorenzo Pieralisi EXPORT_SYMBOL_GPL(cci_ace_get_port); 172ed69bdd8SLorenzo Pieralisi 173b91c8f28SPunit Agrawal static void cci_ace_init_ports(void) 174ed69bdd8SLorenzo Pieralisi { 17578b4d6e0SSudeep KarkadaNagesha int port, cpu; 17678b4d6e0SSudeep KarkadaNagesha struct device_node *cpun; 177ed69bdd8SLorenzo Pieralisi 178ed69bdd8SLorenzo Pieralisi /* 179ed69bdd8SLorenzo Pieralisi * Port index look-up speeds up the function disabling ports by CPU, 180ed69bdd8SLorenzo Pieralisi * since the logical to port index mapping is done once and does 181ed69bdd8SLorenzo Pieralisi * not change after system boot. 182ed69bdd8SLorenzo Pieralisi * The stashed index array is initialized for all possible CPUs 183ed69bdd8SLorenzo Pieralisi * at probe time. 184ed69bdd8SLorenzo Pieralisi */ 18578b4d6e0SSudeep KarkadaNagesha for_each_possible_cpu(cpu) { 18678b4d6e0SSudeep KarkadaNagesha /* too early to use cpu->of_node */ 18778b4d6e0SSudeep KarkadaNagesha cpun = of_get_cpu_node(cpu, NULL); 18878b4d6e0SSudeep KarkadaNagesha 18978b4d6e0SSudeep KarkadaNagesha if (WARN(!cpun, "Missing cpu device node\n")) 190ed69bdd8SLorenzo Pieralisi continue; 191ed69bdd8SLorenzo Pieralisi 192ed69bdd8SLorenzo Pieralisi port = __cci_ace_get_port(cpun, ACE_PORT); 193ed69bdd8SLorenzo Pieralisi if (port < 0) 194ed69bdd8SLorenzo Pieralisi continue; 195ed69bdd8SLorenzo Pieralisi 196ed69bdd8SLorenzo Pieralisi init_cpu_port(&cpu_port[cpu], port, cpu_logical_map(cpu)); 197ed69bdd8SLorenzo Pieralisi } 198ed69bdd8SLorenzo Pieralisi 199ed69bdd8SLorenzo Pieralisi for_each_possible_cpu(cpu) { 200ed69bdd8SLorenzo Pieralisi WARN(!cpu_port_is_valid(&cpu_port[cpu]), 201ed69bdd8SLorenzo Pieralisi "CPU %u does not have an associated CCI port\n", 202ed69bdd8SLorenzo Pieralisi cpu); 203ed69bdd8SLorenzo Pieralisi } 204ed69bdd8SLorenzo Pieralisi } 205ed69bdd8SLorenzo Pieralisi /* 206ed69bdd8SLorenzo Pieralisi * Functions to enable/disable a CCI interconnect slave port 207ed69bdd8SLorenzo Pieralisi * 208ed69bdd8SLorenzo Pieralisi * They are called by low-level power management code to disable slave 209ed69bdd8SLorenzo Pieralisi * interfaces snoops and DVM broadcast. 210ed69bdd8SLorenzo Pieralisi * Since they may execute with cache data allocation disabled and 211ed69bdd8SLorenzo Pieralisi * after the caches have been cleaned and invalidated the functions provide 212ed69bdd8SLorenzo Pieralisi * no explicit locking since they may run with D-cache disabled, so normal 213ed69bdd8SLorenzo Pieralisi * cacheable kernel locks based on ldrex/strex may not work. 214ed69bdd8SLorenzo Pieralisi * Locking has to be provided by BSP implementations to ensure proper 215ed69bdd8SLorenzo Pieralisi * operations. 216ed69bdd8SLorenzo Pieralisi */ 217ed69bdd8SLorenzo Pieralisi 218ed69bdd8SLorenzo Pieralisi /** 219ed69bdd8SLorenzo Pieralisi * cci_port_control() - function to control a CCI port 220ed69bdd8SLorenzo Pieralisi * 221ed69bdd8SLorenzo Pieralisi * @port: index of the port to setup 222ed69bdd8SLorenzo Pieralisi * @enable: if true enables the port, if false disables it 223ed69bdd8SLorenzo Pieralisi */ 224ed69bdd8SLorenzo Pieralisi static void notrace cci_port_control(unsigned int port, bool enable) 225ed69bdd8SLorenzo Pieralisi { 226ed69bdd8SLorenzo Pieralisi void __iomem *base = ports[port].base; 227ed69bdd8SLorenzo Pieralisi 228ed69bdd8SLorenzo Pieralisi writel_relaxed(enable ? CCI_ENABLE_REQ : 0, base + CCI_PORT_CTRL); 229ed69bdd8SLorenzo Pieralisi /* 230ed69bdd8SLorenzo Pieralisi * This function is called from power down procedures 231ed69bdd8SLorenzo Pieralisi * and must not execute any instruction that might 232ed69bdd8SLorenzo Pieralisi * cause the processor to be put in a quiescent state 233ed69bdd8SLorenzo Pieralisi * (eg wfi). Hence, cpu_relax() can not be added to this 234ed69bdd8SLorenzo Pieralisi * read loop to optimize power, since it might hide possibly 235ed69bdd8SLorenzo Pieralisi * disruptive operations. 236ed69bdd8SLorenzo Pieralisi */ 237ed69bdd8SLorenzo Pieralisi while (readl_relaxed(cci_ctrl_base + CCI_CTRL_STATUS) & 0x1) 238ed69bdd8SLorenzo Pieralisi ; 239ed69bdd8SLorenzo Pieralisi } 240ed69bdd8SLorenzo Pieralisi 241ed69bdd8SLorenzo Pieralisi /** 242ed69bdd8SLorenzo Pieralisi * cci_disable_port_by_cpu() - function to disable a CCI port by CPU 243ed69bdd8SLorenzo Pieralisi * reference 244ed69bdd8SLorenzo Pieralisi * 245ed69bdd8SLorenzo Pieralisi * @mpidr: mpidr of the CPU whose CCI port should be disabled 246ed69bdd8SLorenzo Pieralisi * 247ed69bdd8SLorenzo Pieralisi * Disabling a CCI port for a CPU implies disabling the CCI port 248ed69bdd8SLorenzo Pieralisi * controlling that CPU cluster. Code disabling CPU CCI ports 249ed69bdd8SLorenzo Pieralisi * must make sure that the CPU running the code is the last active CPU 250ed69bdd8SLorenzo Pieralisi * in the cluster ie all other CPUs are quiescent in a low power state. 251ed69bdd8SLorenzo Pieralisi * 252ed69bdd8SLorenzo Pieralisi * Return: 253ed69bdd8SLorenzo Pieralisi * 0 on success 254ed69bdd8SLorenzo Pieralisi * -ENODEV on port look-up failure 255ed69bdd8SLorenzo Pieralisi */ 256ed69bdd8SLorenzo Pieralisi int notrace cci_disable_port_by_cpu(u64 mpidr) 257ed69bdd8SLorenzo Pieralisi { 258ed69bdd8SLorenzo Pieralisi int cpu; 259ed69bdd8SLorenzo Pieralisi bool is_valid; 260ed69bdd8SLorenzo Pieralisi for (cpu = 0; cpu < nr_cpu_ids; cpu++) { 261ed69bdd8SLorenzo Pieralisi is_valid = cpu_port_is_valid(&cpu_port[cpu]); 262ed69bdd8SLorenzo Pieralisi if (is_valid && cpu_port_match(&cpu_port[cpu], mpidr)) { 263ed69bdd8SLorenzo Pieralisi cci_port_control(cpu_port[cpu].port, false); 264ed69bdd8SLorenzo Pieralisi return 0; 265ed69bdd8SLorenzo Pieralisi } 266ed69bdd8SLorenzo Pieralisi } 267ed69bdd8SLorenzo Pieralisi return -ENODEV; 268ed69bdd8SLorenzo Pieralisi } 269ed69bdd8SLorenzo Pieralisi EXPORT_SYMBOL_GPL(cci_disable_port_by_cpu); 270ed69bdd8SLorenzo Pieralisi 271ed69bdd8SLorenzo Pieralisi /** 27262158f81SNicolas Pitre * cci_enable_port_for_self() - enable a CCI port for calling CPU 27362158f81SNicolas Pitre * 27462158f81SNicolas Pitre * Enabling a CCI port for the calling CPU implies enabling the CCI 27562158f81SNicolas Pitre * port controlling that CPU's cluster. Caller must make sure that the 27662158f81SNicolas Pitre * CPU running the code is the first active CPU in the cluster and all 27762158f81SNicolas Pitre * other CPUs are quiescent in a low power state or waiting for this CPU 27862158f81SNicolas Pitre * to complete the CCI initialization. 27962158f81SNicolas Pitre * 28062158f81SNicolas Pitre * Because this is called when the MMU is still off and with no stack, 28162158f81SNicolas Pitre * the code must be position independent and ideally rely on callee 28262158f81SNicolas Pitre * clobbered registers only. To achieve this we must code this function 28362158f81SNicolas Pitre * entirely in assembler. 28462158f81SNicolas Pitre * 28562158f81SNicolas Pitre * On success this returns with the proper CCI port enabled. In case of 28662158f81SNicolas Pitre * any failure this never returns as the inability to enable the CCI is 28762158f81SNicolas Pitre * fatal and there is no possible recovery at this stage. 28862158f81SNicolas Pitre */ 28962158f81SNicolas Pitre asmlinkage void __naked cci_enable_port_for_self(void) 29062158f81SNicolas Pitre { 29162158f81SNicolas Pitre asm volatile ("\n" 292f4902492SArnd Bergmann " .arch armv7-a\n" 29362158f81SNicolas Pitre " mrc p15, 0, r0, c0, c0, 5 @ get MPIDR value \n" 29462158f81SNicolas Pitre " and r0, r0, #"__stringify(MPIDR_HWID_BITMASK)" \n" 29562158f81SNicolas Pitre " adr r1, 5f \n" 29662158f81SNicolas Pitre " ldr r2, [r1] \n" 29762158f81SNicolas Pitre " add r1, r1, r2 @ &cpu_port \n" 29862158f81SNicolas Pitre " add ip, r1, %[sizeof_cpu_port] \n" 29962158f81SNicolas Pitre 30062158f81SNicolas Pitre /* Loop over the cpu_port array looking for a matching MPIDR */ 30162158f81SNicolas Pitre "1: ldr r2, [r1, %[offsetof_cpu_port_mpidr_lsb]] \n" 30262158f81SNicolas Pitre " cmp r2, r0 @ compare MPIDR \n" 30362158f81SNicolas Pitre " bne 2f \n" 30462158f81SNicolas Pitre 30562158f81SNicolas Pitre /* Found a match, now test port validity */ 30662158f81SNicolas Pitre " ldr r3, [r1, %[offsetof_cpu_port_port]] \n" 30762158f81SNicolas Pitre " tst r3, #"__stringify(PORT_VALID)" \n" 30862158f81SNicolas Pitre " bne 3f \n" 30962158f81SNicolas Pitre 31062158f81SNicolas Pitre /* no match, loop with the next cpu_port entry */ 31162158f81SNicolas Pitre "2: add r1, r1, %[sizeof_struct_cpu_port] \n" 31262158f81SNicolas Pitre " cmp r1, ip @ done? \n" 31362158f81SNicolas Pitre " blo 1b \n" 31462158f81SNicolas Pitre 31562158f81SNicolas Pitre /* CCI port not found -- cheaply try to stall this CPU */ 31662158f81SNicolas Pitre "cci_port_not_found: \n" 31762158f81SNicolas Pitre " wfi \n" 31862158f81SNicolas Pitre " wfe \n" 31962158f81SNicolas Pitre " b cci_port_not_found \n" 32062158f81SNicolas Pitre 32162158f81SNicolas Pitre /* Use matched port index to look up the corresponding ports entry */ 32262158f81SNicolas Pitre "3: bic r3, r3, #"__stringify(PORT_VALID)" \n" 32362158f81SNicolas Pitre " adr r0, 6f \n" 32462158f81SNicolas Pitre " ldmia r0, {r1, r2} \n" 32562158f81SNicolas Pitre " sub r1, r1, r0 @ virt - phys \n" 32662158f81SNicolas Pitre " ldr r0, [r0, r2] @ *(&ports) \n" 32762158f81SNicolas Pitre " mov r2, %[sizeof_struct_ace_port] \n" 32862158f81SNicolas Pitre " mla r0, r2, r3, r0 @ &ports[index] \n" 32962158f81SNicolas Pitre " sub r0, r0, r1 @ virt_to_phys() \n" 33062158f81SNicolas Pitre 33162158f81SNicolas Pitre /* Enable the CCI port */ 33262158f81SNicolas Pitre " ldr r0, [r0, %[offsetof_port_phys]] \n" 333fdb07aeeSVictor Kamensky " mov r3, %[cci_enable_req]\n" 33462158f81SNicolas Pitre " str r3, [r0, #"__stringify(CCI_PORT_CTRL)"] \n" 33562158f81SNicolas Pitre 33662158f81SNicolas Pitre /* poll the status reg for completion */ 33762158f81SNicolas Pitre " adr r1, 7f \n" 33862158f81SNicolas Pitre " ldr r0, [r1] \n" 33962158f81SNicolas Pitre " ldr r0, [r0, r1] @ cci_ctrl_base \n" 34062158f81SNicolas Pitre "4: ldr r1, [r0, #"__stringify(CCI_CTRL_STATUS)"] \n" 341fdb07aeeSVictor Kamensky " tst r1, %[cci_control_status_bits] \n" 34262158f81SNicolas Pitre " bne 4b \n" 34362158f81SNicolas Pitre 34462158f81SNicolas Pitre " mov r0, #0 \n" 34562158f81SNicolas Pitre " bx lr \n" 34662158f81SNicolas Pitre 34762158f81SNicolas Pitre " .align 2 \n" 34862158f81SNicolas Pitre "5: .word cpu_port - . \n" 34962158f81SNicolas Pitre "6: .word . \n" 35062158f81SNicolas Pitre " .word ports - 6b \n" 35162158f81SNicolas Pitre "7: .word cci_ctrl_phys - . \n" 35262158f81SNicolas Pitre : : 35362158f81SNicolas Pitre [sizeof_cpu_port] "i" (sizeof(cpu_port)), 354fdb07aeeSVictor Kamensky [cci_enable_req] "i" cpu_to_le32(CCI_ENABLE_REQ), 355fdb07aeeSVictor Kamensky [cci_control_status_bits] "i" cpu_to_le32(1), 35662158f81SNicolas Pitre #ifndef __ARMEB__ 35762158f81SNicolas Pitre [offsetof_cpu_port_mpidr_lsb] "i" (offsetof(struct cpu_port, mpidr)), 35862158f81SNicolas Pitre #else 35962158f81SNicolas Pitre [offsetof_cpu_port_mpidr_lsb] "i" (offsetof(struct cpu_port, mpidr)+4), 36062158f81SNicolas Pitre #endif 36162158f81SNicolas Pitre [offsetof_cpu_port_port] "i" (offsetof(struct cpu_port, port)), 36262158f81SNicolas Pitre [sizeof_struct_cpu_port] "i" (sizeof(struct cpu_port)), 36362158f81SNicolas Pitre [sizeof_struct_ace_port] "i" (sizeof(struct cci_ace_port)), 36462158f81SNicolas Pitre [offsetof_port_phys] "i" (offsetof(struct cci_ace_port, phys)) ); 36562158f81SNicolas Pitre 36662158f81SNicolas Pitre unreachable(); 36762158f81SNicolas Pitre } 36862158f81SNicolas Pitre 36962158f81SNicolas Pitre /** 370ed69bdd8SLorenzo Pieralisi * __cci_control_port_by_device() - function to control a CCI port by device 371ed69bdd8SLorenzo Pieralisi * reference 372ed69bdd8SLorenzo Pieralisi * 373ed69bdd8SLorenzo Pieralisi * @dn: device node pointer of the device whose CCI port should be 374ed69bdd8SLorenzo Pieralisi * controlled 375ed69bdd8SLorenzo Pieralisi * @enable: if true enables the port, if false disables it 376ed69bdd8SLorenzo Pieralisi * 377ed69bdd8SLorenzo Pieralisi * Return: 378ed69bdd8SLorenzo Pieralisi * 0 on success 379ed69bdd8SLorenzo Pieralisi * -ENODEV on port look-up failure 380ed69bdd8SLorenzo Pieralisi */ 381ed69bdd8SLorenzo Pieralisi int notrace __cci_control_port_by_device(struct device_node *dn, bool enable) 382ed69bdd8SLorenzo Pieralisi { 383ed69bdd8SLorenzo Pieralisi int port; 384ed69bdd8SLorenzo Pieralisi 385ed69bdd8SLorenzo Pieralisi if (!dn) 386ed69bdd8SLorenzo Pieralisi return -ENODEV; 387ed69bdd8SLorenzo Pieralisi 388ed69bdd8SLorenzo Pieralisi port = __cci_ace_get_port(dn, ACE_LITE_PORT); 3899c0982d8SRob Herring if (WARN_ONCE(port < 0, "node %pOF ACE lite port look-up failure\n", 3909c0982d8SRob Herring dn)) 391ed69bdd8SLorenzo Pieralisi return -ENODEV; 392ed69bdd8SLorenzo Pieralisi cci_port_control(port, enable); 393ed69bdd8SLorenzo Pieralisi return 0; 394ed69bdd8SLorenzo Pieralisi } 395ed69bdd8SLorenzo Pieralisi EXPORT_SYMBOL_GPL(__cci_control_port_by_device); 396ed69bdd8SLorenzo Pieralisi 397ed69bdd8SLorenzo Pieralisi /** 398ed69bdd8SLorenzo Pieralisi * __cci_control_port_by_index() - function to control a CCI port by port index 399ed69bdd8SLorenzo Pieralisi * 400ed69bdd8SLorenzo Pieralisi * @port: port index previously retrieved with cci_ace_get_port() 401ed69bdd8SLorenzo Pieralisi * @enable: if true enables the port, if false disables it 402ed69bdd8SLorenzo Pieralisi * 403ed69bdd8SLorenzo Pieralisi * Return: 404ed69bdd8SLorenzo Pieralisi * 0 on success 405ed69bdd8SLorenzo Pieralisi * -ENODEV on port index out of range 406ed69bdd8SLorenzo Pieralisi * -EPERM if operation carried out on an ACE PORT 407ed69bdd8SLorenzo Pieralisi */ 408ed69bdd8SLorenzo Pieralisi int notrace __cci_control_port_by_index(u32 port, bool enable) 409ed69bdd8SLorenzo Pieralisi { 410ed69bdd8SLorenzo Pieralisi if (port >= nb_cci_ports || ports[port].type == ACE_INVALID_PORT) 411ed69bdd8SLorenzo Pieralisi return -ENODEV; 412ed69bdd8SLorenzo Pieralisi /* 413ed69bdd8SLorenzo Pieralisi * CCI control for ports connected to CPUS is extremely fragile 414ed69bdd8SLorenzo Pieralisi * and must be made to go through a specific and controlled 415ed69bdd8SLorenzo Pieralisi * interface (ie cci_disable_port_by_cpu(); control by general purpose 416ed69bdd8SLorenzo Pieralisi * indexing is therefore disabled for ACE ports. 417ed69bdd8SLorenzo Pieralisi */ 418ed69bdd8SLorenzo Pieralisi if (ports[port].type == ACE_PORT) 419ed69bdd8SLorenzo Pieralisi return -EPERM; 420ed69bdd8SLorenzo Pieralisi 421ed69bdd8SLorenzo Pieralisi cci_port_control(port, enable); 422ed69bdd8SLorenzo Pieralisi return 0; 423ed69bdd8SLorenzo Pieralisi } 424ed69bdd8SLorenzo Pieralisi EXPORT_SYMBOL_GPL(__cci_control_port_by_index); 425ed69bdd8SLorenzo Pieralisi 426ed69bdd8SLorenzo Pieralisi static const struct of_device_id arm_cci_ctrl_if_matches[] = { 427ed69bdd8SLorenzo Pieralisi {.compatible = "arm,cci-400-ctrl-if", }, 428ed69bdd8SLorenzo Pieralisi {}, 429ed69bdd8SLorenzo Pieralisi }; 430ed69bdd8SLorenzo Pieralisi 431f6b9e83cSSuzuki K. Poulose static int cci_probe_ports(struct device_node *np) 432ed69bdd8SLorenzo Pieralisi { 433ed69bdd8SLorenzo Pieralisi struct cci_nb_ports const *cci_config; 434ed69bdd8SLorenzo Pieralisi int ret, i, nb_ace = 0, nb_ace_lite = 0; 435f6b9e83cSSuzuki K. Poulose struct device_node *cp; 43662158f81SNicolas Pitre struct resource res; 437ed69bdd8SLorenzo Pieralisi const char *match_str; 438ed69bdd8SLorenzo Pieralisi bool is_ace; 439ed69bdd8SLorenzo Pieralisi 440896ddd60SAbhilash Kesavan 441ed69bdd8SLorenzo Pieralisi cci_config = of_match_node(arm_cci_matches, np)->data; 442ed69bdd8SLorenzo Pieralisi if (!cci_config) 443ed69bdd8SLorenzo Pieralisi return -ENODEV; 444ed69bdd8SLorenzo Pieralisi 445ed69bdd8SLorenzo Pieralisi nb_cci_ports = cci_config->nb_ace + cci_config->nb_ace_lite; 446ed69bdd8SLorenzo Pieralisi 4477c762036SLorenzo Pieralisi ports = kcalloc(nb_cci_ports, sizeof(*ports), GFP_KERNEL); 448ed69bdd8SLorenzo Pieralisi if (!ports) 449ed69bdd8SLorenzo Pieralisi return -ENOMEM; 450ed69bdd8SLorenzo Pieralisi 4513ee5e821SRobin Murphy for_each_available_child_of_node(np, cp) { 452ed69bdd8SLorenzo Pieralisi if (!of_match_node(arm_cci_ctrl_if_matches, cp)) 453ed69bdd8SLorenzo Pieralisi continue; 454ed69bdd8SLorenzo Pieralisi 455ed69bdd8SLorenzo Pieralisi i = nb_ace + nb_ace_lite; 456ed69bdd8SLorenzo Pieralisi 457ed69bdd8SLorenzo Pieralisi if (i >= nb_cci_ports) 458ed69bdd8SLorenzo Pieralisi break; 459ed69bdd8SLorenzo Pieralisi 460ed69bdd8SLorenzo Pieralisi if (of_property_read_string(cp, "interface-type", 461ed69bdd8SLorenzo Pieralisi &match_str)) { 4629c0982d8SRob Herring WARN(1, "node %pOF missing interface-type property\n", 4639c0982d8SRob Herring cp); 464ed69bdd8SLorenzo Pieralisi continue; 465ed69bdd8SLorenzo Pieralisi } 466ed69bdd8SLorenzo Pieralisi is_ace = strcmp(match_str, "ace") == 0; 467ed69bdd8SLorenzo Pieralisi if (!is_ace && strcmp(match_str, "ace-lite")) { 4689c0982d8SRob Herring WARN(1, "node %pOF containing invalid interface-type property, skipping it\n", 4699c0982d8SRob Herring cp); 470ed69bdd8SLorenzo Pieralisi continue; 471ed69bdd8SLorenzo Pieralisi } 472ed69bdd8SLorenzo Pieralisi 47362158f81SNicolas Pitre ret = of_address_to_resource(cp, 0, &res); 47462158f81SNicolas Pitre if (!ret) { 47562158f81SNicolas Pitre ports[i].base = ioremap(res.start, resource_size(&res)); 47662158f81SNicolas Pitre ports[i].phys = res.start; 47762158f81SNicolas Pitre } 47862158f81SNicolas Pitre if (ret || !ports[i].base) { 479ed69bdd8SLorenzo Pieralisi WARN(1, "unable to ioremap CCI port %d\n", i); 480ed69bdd8SLorenzo Pieralisi continue; 481ed69bdd8SLorenzo Pieralisi } 482ed69bdd8SLorenzo Pieralisi 483ed69bdd8SLorenzo Pieralisi if (is_ace) { 484ed69bdd8SLorenzo Pieralisi if (WARN_ON(nb_ace >= cci_config->nb_ace)) 485ed69bdd8SLorenzo Pieralisi continue; 486ed69bdd8SLorenzo Pieralisi ports[i].type = ACE_PORT; 487ed69bdd8SLorenzo Pieralisi ++nb_ace; 488ed69bdd8SLorenzo Pieralisi } else { 489ed69bdd8SLorenzo Pieralisi if (WARN_ON(nb_ace_lite >= cci_config->nb_ace_lite)) 490ed69bdd8SLorenzo Pieralisi continue; 491ed69bdd8SLorenzo Pieralisi ports[i].type = ACE_LITE_PORT; 492ed69bdd8SLorenzo Pieralisi ++nb_ace_lite; 493ed69bdd8SLorenzo Pieralisi } 494ed69bdd8SLorenzo Pieralisi ports[i].dn = cp; 495ed69bdd8SLorenzo Pieralisi } 496ed69bdd8SLorenzo Pieralisi 497801f33beSLorenzo Pieralisi /* 498801f33beSLorenzo Pieralisi * If there is no CCI port that is under kernel control 499801f33beSLorenzo Pieralisi * return early and report probe status. 500801f33beSLorenzo Pieralisi */ 501801f33beSLorenzo Pieralisi if (!nb_ace && !nb_ace_lite) 502801f33beSLorenzo Pieralisi return -ENODEV; 503801f33beSLorenzo Pieralisi 504ed69bdd8SLorenzo Pieralisi /* initialize a stashed array of ACE ports to speed-up look-up */ 505ed69bdd8SLorenzo Pieralisi cci_ace_init_ports(); 506ed69bdd8SLorenzo Pieralisi 507ed69bdd8SLorenzo Pieralisi /* 508ed69bdd8SLorenzo Pieralisi * Multi-cluster systems may need this data when non-coherent, during 509ed69bdd8SLorenzo Pieralisi * cluster power-up/power-down. Make sure it reaches main memory. 510ed69bdd8SLorenzo Pieralisi */ 511ed69bdd8SLorenzo Pieralisi sync_cache_w(&cci_ctrl_base); 51262158f81SNicolas Pitre sync_cache_w(&cci_ctrl_phys); 513ed69bdd8SLorenzo Pieralisi sync_cache_w(&ports); 514ed69bdd8SLorenzo Pieralisi sync_cache_w(&cpu_port); 515ed69bdd8SLorenzo Pieralisi __sync_cache_range_w(ports, sizeof(*ports) * nb_cci_ports); 516ed69bdd8SLorenzo Pieralisi pr_info("ARM CCI driver probed\n"); 517f6b9e83cSSuzuki K. Poulose 518ed69bdd8SLorenzo Pieralisi return 0; 519f6b9e83cSSuzuki K. Poulose } 520ee8e5d5fSSuzuki K. Poulose #else /* !CONFIG_ARM_CCI400_PORT_CTRL */ 521ee8e5d5fSSuzuki K. Poulose static inline int cci_probe_ports(struct device_node *np) 522ee8e5d5fSSuzuki K. Poulose { 523ee8e5d5fSSuzuki K. Poulose return 0; 524ee8e5d5fSSuzuki K. Poulose } 525ee8e5d5fSSuzuki K. Poulose #endif /* CONFIG_ARM_CCI400_PORT_CTRL */ 526ed69bdd8SLorenzo Pieralisi 527f6b9e83cSSuzuki K. Poulose static int cci_probe(void) 528f6b9e83cSSuzuki K. Poulose { 529f6b9e83cSSuzuki K. Poulose int ret; 530f6b9e83cSSuzuki K. Poulose struct device_node *np; 531f6b9e83cSSuzuki K. Poulose struct resource res; 532ed69bdd8SLorenzo Pieralisi 533f6b9e83cSSuzuki K. Poulose np = of_find_matching_node(NULL, arm_cci_matches); 5343ee5e821SRobin Murphy if (!of_device_is_available(np)) 535f6b9e83cSSuzuki K. Poulose return -ENODEV; 536f6b9e83cSSuzuki K. Poulose 537f6b9e83cSSuzuki K. Poulose ret = of_address_to_resource(np, 0, &res); 538f6b9e83cSSuzuki K. Poulose if (!ret) { 539f6b9e83cSSuzuki K. Poulose cci_ctrl_base = ioremap(res.start, resource_size(&res)); 540f6b9e83cSSuzuki K. Poulose cci_ctrl_phys = res.start; 541f6b9e83cSSuzuki K. Poulose } 542f6b9e83cSSuzuki K. Poulose if (ret || !cci_ctrl_base) { 543f6b9e83cSSuzuki K. Poulose WARN(1, "unable to ioremap CCI ctrl\n"); 544f6b9e83cSSuzuki K. Poulose return -ENXIO; 545f6b9e83cSSuzuki K. Poulose } 546f6b9e83cSSuzuki K. Poulose 547f6b9e83cSSuzuki K. Poulose return cci_probe_ports(np); 548ed69bdd8SLorenzo Pieralisi } 549ed69bdd8SLorenzo Pieralisi 550ed69bdd8SLorenzo Pieralisi static int cci_init_status = -EAGAIN; 551ed69bdd8SLorenzo Pieralisi static DEFINE_MUTEX(cci_probing); 552ed69bdd8SLorenzo Pieralisi 553b91c8f28SPunit Agrawal static int cci_init(void) 554ed69bdd8SLorenzo Pieralisi { 555ed69bdd8SLorenzo Pieralisi if (cci_init_status != -EAGAIN) 556ed69bdd8SLorenzo Pieralisi return cci_init_status; 557ed69bdd8SLorenzo Pieralisi 558ed69bdd8SLorenzo Pieralisi mutex_lock(&cci_probing); 559ed69bdd8SLorenzo Pieralisi if (cci_init_status == -EAGAIN) 560ed69bdd8SLorenzo Pieralisi cci_init_status = cci_probe(); 561ed69bdd8SLorenzo Pieralisi mutex_unlock(&cci_probing); 562ed69bdd8SLorenzo Pieralisi return cci_init_status; 563ed69bdd8SLorenzo Pieralisi } 564ed69bdd8SLorenzo Pieralisi 565ed69bdd8SLorenzo Pieralisi /* 566ed69bdd8SLorenzo Pieralisi * To sort out early init calls ordering a helper function is provided to 567ed69bdd8SLorenzo Pieralisi * check if the CCI driver has beed initialized. Function check if the driver 568ed69bdd8SLorenzo Pieralisi * has been initialized, if not it calls the init function that probes 569ed69bdd8SLorenzo Pieralisi * the driver and updates the return value. 570ed69bdd8SLorenzo Pieralisi */ 571b91c8f28SPunit Agrawal bool cci_probed(void) 572ed69bdd8SLorenzo Pieralisi { 573ed69bdd8SLorenzo Pieralisi return cci_init() == 0; 574ed69bdd8SLorenzo Pieralisi } 575ed69bdd8SLorenzo Pieralisi EXPORT_SYMBOL_GPL(cci_probed); 576ed69bdd8SLorenzo Pieralisi 577ed69bdd8SLorenzo Pieralisi early_initcall(cci_init); 578b91c8f28SPunit Agrawal core_initcall(cci_platform_init); 579ed69bdd8SLorenzo Pieralisi MODULE_LICENSE("GPL"); 580ed69bdd8SLorenzo Pieralisi MODULE_DESCRIPTION("ARM CCI support"); 581