18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * CCI cache coherent interconnect driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2013 ARM Ltd. 58c2ecf20Sopenharmony_ci * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 88c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License version 2 as 98c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any 128c2ecf20Sopenharmony_ci * kind, whether express or implied; without even the implied warranty 138c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 148c2ecf20Sopenharmony_ci * GNU General Public License for more details. 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/arm-cci.h> 188c2ecf20Sopenharmony_ci#include <linux/io.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/of_address.h> 218c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 228c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 268c2ecf20Sopenharmony_ci#include <asm/smp_plat.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic void __iomem *cci_ctrl_base __ro_after_init; 298c2ecf20Sopenharmony_cistatic unsigned long cci_ctrl_phys __ro_after_init; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_CCI400_PORT_CTRL 328c2ecf20Sopenharmony_cistruct cci_nb_ports { 338c2ecf20Sopenharmony_ci unsigned int nb_ace; 348c2ecf20Sopenharmony_ci unsigned int nb_ace_lite; 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic const struct cci_nb_ports cci400_ports = { 388c2ecf20Sopenharmony_ci .nb_ace = 2, 398c2ecf20Sopenharmony_ci .nb_ace_lite = 3 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define CCI400_PORTS_DATA (&cci400_ports) 438c2ecf20Sopenharmony_ci#else 448c2ecf20Sopenharmony_ci#define CCI400_PORTS_DATA (NULL) 458c2ecf20Sopenharmony_ci#endif 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic const struct of_device_id arm_cci_matches[] = { 488c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_CCI400_COMMON 498c2ecf20Sopenharmony_ci {.compatible = "arm,cci-400", .data = CCI400_PORTS_DATA }, 508c2ecf20Sopenharmony_ci#endif 518c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_CCI5xx_PMU 528c2ecf20Sopenharmony_ci { .compatible = "arm,cci-500", }, 538c2ecf20Sopenharmony_ci { .compatible = "arm,cci-550", }, 548c2ecf20Sopenharmony_ci#endif 558c2ecf20Sopenharmony_ci {}, 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic const struct of_dev_auxdata arm_cci_auxdata[] = { 598c2ecf20Sopenharmony_ci OF_DEV_AUXDATA("arm,cci-400-pmu", 0, NULL, &cci_ctrl_base), 608c2ecf20Sopenharmony_ci OF_DEV_AUXDATA("arm,cci-400-pmu,r0", 0, NULL, &cci_ctrl_base), 618c2ecf20Sopenharmony_ci OF_DEV_AUXDATA("arm,cci-400-pmu,r1", 0, NULL, &cci_ctrl_base), 628c2ecf20Sopenharmony_ci OF_DEV_AUXDATA("arm,cci-500-pmu,r0", 0, NULL, &cci_ctrl_base), 638c2ecf20Sopenharmony_ci OF_DEV_AUXDATA("arm,cci-550-pmu,r0", 0, NULL, &cci_ctrl_base), 648c2ecf20Sopenharmony_ci {} 658c2ecf20Sopenharmony_ci}; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define DRIVER_NAME "ARM-CCI" 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic int cci_platform_probe(struct platform_device *pdev) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci if (!cci_probed()) 728c2ecf20Sopenharmony_ci return -ENODEV; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci return of_platform_populate(pdev->dev.of_node, NULL, 758c2ecf20Sopenharmony_ci arm_cci_auxdata, &pdev->dev); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic struct platform_driver cci_platform_driver = { 798c2ecf20Sopenharmony_ci .driver = { 808c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 818c2ecf20Sopenharmony_ci .of_match_table = arm_cci_matches, 828c2ecf20Sopenharmony_ci }, 838c2ecf20Sopenharmony_ci .probe = cci_platform_probe, 848c2ecf20Sopenharmony_ci}; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic int __init cci_platform_init(void) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci return platform_driver_register(&cci_platform_driver); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_CCI400_PORT_CTRL 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci#define CCI_PORT_CTRL 0x0 948c2ecf20Sopenharmony_ci#define CCI_CTRL_STATUS 0xc 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci#define CCI_ENABLE_SNOOP_REQ 0x1 978c2ecf20Sopenharmony_ci#define CCI_ENABLE_DVM_REQ 0x2 988c2ecf20Sopenharmony_ci#define CCI_ENABLE_REQ (CCI_ENABLE_SNOOP_REQ | CCI_ENABLE_DVM_REQ) 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cienum cci_ace_port_type { 1018c2ecf20Sopenharmony_ci ACE_INVALID_PORT = 0x0, 1028c2ecf20Sopenharmony_ci ACE_PORT, 1038c2ecf20Sopenharmony_ci ACE_LITE_PORT, 1048c2ecf20Sopenharmony_ci}; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistruct cci_ace_port { 1078c2ecf20Sopenharmony_ci void __iomem *base; 1088c2ecf20Sopenharmony_ci unsigned long phys; 1098c2ecf20Sopenharmony_ci enum cci_ace_port_type type; 1108c2ecf20Sopenharmony_ci struct device_node *dn; 1118c2ecf20Sopenharmony_ci}; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic struct cci_ace_port *ports; 1148c2ecf20Sopenharmony_cistatic unsigned int nb_cci_ports; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistruct cpu_port { 1178c2ecf20Sopenharmony_ci u64 mpidr; 1188c2ecf20Sopenharmony_ci u32 port; 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* 1228c2ecf20Sopenharmony_ci * Use the port MSB as valid flag, shift can be made dynamic 1238c2ecf20Sopenharmony_ci * by computing number of bits required for port indexes. 1248c2ecf20Sopenharmony_ci * Code disabling CCI cpu ports runs with D-cache invalidated 1258c2ecf20Sopenharmony_ci * and SCTLR bit clear so data accesses must be kept to a minimum 1268c2ecf20Sopenharmony_ci * to improve performance; for now shift is left static to 1278c2ecf20Sopenharmony_ci * avoid one more data access while disabling the CCI port. 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_ci#define PORT_VALID_SHIFT 31 1308c2ecf20Sopenharmony_ci#define PORT_VALID (0x1 << PORT_VALID_SHIFT) 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic inline void init_cpu_port(struct cpu_port *port, u32 index, u64 mpidr) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci port->port = PORT_VALID | index; 1358c2ecf20Sopenharmony_ci port->mpidr = mpidr; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic inline bool cpu_port_is_valid(struct cpu_port *port) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci return !!(port->port & PORT_VALID); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic inline bool cpu_port_match(struct cpu_port *port, u64 mpidr) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci return port->mpidr == (mpidr & MPIDR_HWID_BITMASK); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic struct cpu_port cpu_port[NR_CPUS]; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci/** 1518c2ecf20Sopenharmony_ci * __cci_ace_get_port - Function to retrieve the port index connected to 1528c2ecf20Sopenharmony_ci * a cpu or device. 1538c2ecf20Sopenharmony_ci * 1548c2ecf20Sopenharmony_ci * @dn: device node of the device to look-up 1558c2ecf20Sopenharmony_ci * @type: port type 1568c2ecf20Sopenharmony_ci * 1578c2ecf20Sopenharmony_ci * Return value: 1588c2ecf20Sopenharmony_ci * - CCI port index if success 1598c2ecf20Sopenharmony_ci * - -ENODEV if failure 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_cistatic int __cci_ace_get_port(struct device_node *dn, int type) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci int i; 1648c2ecf20Sopenharmony_ci bool ace_match; 1658c2ecf20Sopenharmony_ci struct device_node *cci_portn; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci cci_portn = of_parse_phandle(dn, "cci-control-port", 0); 1688c2ecf20Sopenharmony_ci for (i = 0; i < nb_cci_ports; i++) { 1698c2ecf20Sopenharmony_ci ace_match = ports[i].type == type; 1708c2ecf20Sopenharmony_ci if (ace_match && cci_portn == ports[i].dn) 1718c2ecf20Sopenharmony_ci return i; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci return -ENODEV; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ciint cci_ace_get_port(struct device_node *dn) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci return __cci_ace_get_port(dn, ACE_LITE_PORT); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cci_ace_get_port); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic void cci_ace_init_ports(void) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci int port, cpu; 1858c2ecf20Sopenharmony_ci struct device_node *cpun; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* 1888c2ecf20Sopenharmony_ci * Port index look-up speeds up the function disabling ports by CPU, 1898c2ecf20Sopenharmony_ci * since the logical to port index mapping is done once and does 1908c2ecf20Sopenharmony_ci * not change after system boot. 1918c2ecf20Sopenharmony_ci * The stashed index array is initialized for all possible CPUs 1928c2ecf20Sopenharmony_ci * at probe time. 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 1958c2ecf20Sopenharmony_ci /* too early to use cpu->of_node */ 1968c2ecf20Sopenharmony_ci cpun = of_get_cpu_node(cpu, NULL); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (WARN(!cpun, "Missing cpu device node\n")) 1998c2ecf20Sopenharmony_ci continue; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci port = __cci_ace_get_port(cpun, ACE_PORT); 2028c2ecf20Sopenharmony_ci if (port < 0) 2038c2ecf20Sopenharmony_ci continue; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci init_cpu_port(&cpu_port[cpu], port, cpu_logical_map(cpu)); 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 2098c2ecf20Sopenharmony_ci WARN(!cpu_port_is_valid(&cpu_port[cpu]), 2108c2ecf20Sopenharmony_ci "CPU %u does not have an associated CCI port\n", 2118c2ecf20Sopenharmony_ci cpu); 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci/* 2158c2ecf20Sopenharmony_ci * Functions to enable/disable a CCI interconnect slave port 2168c2ecf20Sopenharmony_ci * 2178c2ecf20Sopenharmony_ci * They are called by low-level power management code to disable slave 2188c2ecf20Sopenharmony_ci * interfaces snoops and DVM broadcast. 2198c2ecf20Sopenharmony_ci * Since they may execute with cache data allocation disabled and 2208c2ecf20Sopenharmony_ci * after the caches have been cleaned and invalidated the functions provide 2218c2ecf20Sopenharmony_ci * no explicit locking since they may run with D-cache disabled, so normal 2228c2ecf20Sopenharmony_ci * cacheable kernel locks based on ldrex/strex may not work. 2238c2ecf20Sopenharmony_ci * Locking has to be provided by BSP implementations to ensure proper 2248c2ecf20Sopenharmony_ci * operations. 2258c2ecf20Sopenharmony_ci */ 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci/** 2288c2ecf20Sopenharmony_ci * cci_port_control() - function to control a CCI port 2298c2ecf20Sopenharmony_ci * 2308c2ecf20Sopenharmony_ci * @port: index of the port to setup 2318c2ecf20Sopenharmony_ci * @enable: if true enables the port, if false disables it 2328c2ecf20Sopenharmony_ci */ 2338c2ecf20Sopenharmony_cistatic void notrace cci_port_control(unsigned int port, bool enable) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci void __iomem *base = ports[port].base; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci writel_relaxed(enable ? CCI_ENABLE_REQ : 0, base + CCI_PORT_CTRL); 2388c2ecf20Sopenharmony_ci /* 2398c2ecf20Sopenharmony_ci * This function is called from power down procedures 2408c2ecf20Sopenharmony_ci * and must not execute any instruction that might 2418c2ecf20Sopenharmony_ci * cause the processor to be put in a quiescent state 2428c2ecf20Sopenharmony_ci * (eg wfi). Hence, cpu_relax() can not be added to this 2438c2ecf20Sopenharmony_ci * read loop to optimize power, since it might hide possibly 2448c2ecf20Sopenharmony_ci * disruptive operations. 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_ci while (readl_relaxed(cci_ctrl_base + CCI_CTRL_STATUS) & 0x1) 2478c2ecf20Sopenharmony_ci ; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci/** 2518c2ecf20Sopenharmony_ci * cci_disable_port_by_cpu() - function to disable a CCI port by CPU 2528c2ecf20Sopenharmony_ci * reference 2538c2ecf20Sopenharmony_ci * 2548c2ecf20Sopenharmony_ci * @mpidr: mpidr of the CPU whose CCI port should be disabled 2558c2ecf20Sopenharmony_ci * 2568c2ecf20Sopenharmony_ci * Disabling a CCI port for a CPU implies disabling the CCI port 2578c2ecf20Sopenharmony_ci * controlling that CPU cluster. Code disabling CPU CCI ports 2588c2ecf20Sopenharmony_ci * must make sure that the CPU running the code is the last active CPU 2598c2ecf20Sopenharmony_ci * in the cluster ie all other CPUs are quiescent in a low power state. 2608c2ecf20Sopenharmony_ci * 2618c2ecf20Sopenharmony_ci * Return: 2628c2ecf20Sopenharmony_ci * 0 on success 2638c2ecf20Sopenharmony_ci * -ENODEV on port look-up failure 2648c2ecf20Sopenharmony_ci */ 2658c2ecf20Sopenharmony_ciint notrace cci_disable_port_by_cpu(u64 mpidr) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci int cpu; 2688c2ecf20Sopenharmony_ci bool is_valid; 2698c2ecf20Sopenharmony_ci for (cpu = 0; cpu < nr_cpu_ids; cpu++) { 2708c2ecf20Sopenharmony_ci is_valid = cpu_port_is_valid(&cpu_port[cpu]); 2718c2ecf20Sopenharmony_ci if (is_valid && cpu_port_match(&cpu_port[cpu], mpidr)) { 2728c2ecf20Sopenharmony_ci cci_port_control(cpu_port[cpu].port, false); 2738c2ecf20Sopenharmony_ci return 0; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci return -ENODEV; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cci_disable_port_by_cpu); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci/** 2818c2ecf20Sopenharmony_ci * cci_enable_port_for_self() - enable a CCI port for calling CPU 2828c2ecf20Sopenharmony_ci * 2838c2ecf20Sopenharmony_ci * Enabling a CCI port for the calling CPU implies enabling the CCI 2848c2ecf20Sopenharmony_ci * port controlling that CPU's cluster. Caller must make sure that the 2858c2ecf20Sopenharmony_ci * CPU running the code is the first active CPU in the cluster and all 2868c2ecf20Sopenharmony_ci * other CPUs are quiescent in a low power state or waiting for this CPU 2878c2ecf20Sopenharmony_ci * to complete the CCI initialization. 2888c2ecf20Sopenharmony_ci * 2898c2ecf20Sopenharmony_ci * Because this is called when the MMU is still off and with no stack, 2908c2ecf20Sopenharmony_ci * the code must be position independent and ideally rely on callee 2918c2ecf20Sopenharmony_ci * clobbered registers only. To achieve this we must code this function 2928c2ecf20Sopenharmony_ci * entirely in assembler. 2938c2ecf20Sopenharmony_ci * 2948c2ecf20Sopenharmony_ci * On success this returns with the proper CCI port enabled. In case of 2958c2ecf20Sopenharmony_ci * any failure this never returns as the inability to enable the CCI is 2968c2ecf20Sopenharmony_ci * fatal and there is no possible recovery at this stage. 2978c2ecf20Sopenharmony_ci */ 2988c2ecf20Sopenharmony_ciasmlinkage void __naked cci_enable_port_for_self(void) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci asm volatile ("\n" 3018c2ecf20Sopenharmony_ci" .arch armv7-a\n" 3028c2ecf20Sopenharmony_ci" mrc p15, 0, r0, c0, c0, 5 @ get MPIDR value \n" 3038c2ecf20Sopenharmony_ci" and r0, r0, #"__stringify(MPIDR_HWID_BITMASK)" \n" 3048c2ecf20Sopenharmony_ci" adr r1, 5f \n" 3058c2ecf20Sopenharmony_ci" ldr r2, [r1] \n" 3068c2ecf20Sopenharmony_ci" add r1, r1, r2 @ &cpu_port \n" 3078c2ecf20Sopenharmony_ci" add ip, r1, %[sizeof_cpu_port] \n" 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* Loop over the cpu_port array looking for a matching MPIDR */ 3108c2ecf20Sopenharmony_ci"1: ldr r2, [r1, %[offsetof_cpu_port_mpidr_lsb]] \n" 3118c2ecf20Sopenharmony_ci" cmp r2, r0 @ compare MPIDR \n" 3128c2ecf20Sopenharmony_ci" bne 2f \n" 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* Found a match, now test port validity */ 3158c2ecf20Sopenharmony_ci" ldr r3, [r1, %[offsetof_cpu_port_port]] \n" 3168c2ecf20Sopenharmony_ci" tst r3, #"__stringify(PORT_VALID)" \n" 3178c2ecf20Sopenharmony_ci" bne 3f \n" 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* no match, loop with the next cpu_port entry */ 3208c2ecf20Sopenharmony_ci"2: add r1, r1, %[sizeof_struct_cpu_port] \n" 3218c2ecf20Sopenharmony_ci" cmp r1, ip @ done? \n" 3228c2ecf20Sopenharmony_ci" blo 1b \n" 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* CCI port not found -- cheaply try to stall this CPU */ 3258c2ecf20Sopenharmony_ci"cci_port_not_found: \n" 3268c2ecf20Sopenharmony_ci" wfi \n" 3278c2ecf20Sopenharmony_ci" wfe \n" 3288c2ecf20Sopenharmony_ci" b cci_port_not_found \n" 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* Use matched port index to look up the corresponding ports entry */ 3318c2ecf20Sopenharmony_ci"3: bic r3, r3, #"__stringify(PORT_VALID)" \n" 3328c2ecf20Sopenharmony_ci" adr r0, 6f \n" 3338c2ecf20Sopenharmony_ci" ldmia r0, {r1, r2} \n" 3348c2ecf20Sopenharmony_ci" sub r1, r1, r0 @ virt - phys \n" 3358c2ecf20Sopenharmony_ci" ldr r0, [r0, r2] @ *(&ports) \n" 3368c2ecf20Sopenharmony_ci" mov r2, %[sizeof_struct_ace_port] \n" 3378c2ecf20Sopenharmony_ci" mla r0, r2, r3, r0 @ &ports[index] \n" 3388c2ecf20Sopenharmony_ci" sub r0, r0, r1 @ virt_to_phys() \n" 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* Enable the CCI port */ 3418c2ecf20Sopenharmony_ci" ldr r0, [r0, %[offsetof_port_phys]] \n" 3428c2ecf20Sopenharmony_ci" mov r3, %[cci_enable_req]\n" 3438c2ecf20Sopenharmony_ci" str r3, [r0, #"__stringify(CCI_PORT_CTRL)"] \n" 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci /* poll the status reg for completion */ 3468c2ecf20Sopenharmony_ci" adr r1, 7f \n" 3478c2ecf20Sopenharmony_ci" ldr r0, [r1] \n" 3488c2ecf20Sopenharmony_ci" ldr r0, [r0, r1] @ cci_ctrl_base \n" 3498c2ecf20Sopenharmony_ci"4: ldr r1, [r0, #"__stringify(CCI_CTRL_STATUS)"] \n" 3508c2ecf20Sopenharmony_ci" tst r1, %[cci_control_status_bits] \n" 3518c2ecf20Sopenharmony_ci" bne 4b \n" 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci" mov r0, #0 \n" 3548c2ecf20Sopenharmony_ci" bx lr \n" 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci" .align 2 \n" 3578c2ecf20Sopenharmony_ci"5: .word cpu_port - . \n" 3588c2ecf20Sopenharmony_ci"6: .word . \n" 3598c2ecf20Sopenharmony_ci" .word ports - 6b \n" 3608c2ecf20Sopenharmony_ci"7: .word cci_ctrl_phys - . \n" 3618c2ecf20Sopenharmony_ci : : 3628c2ecf20Sopenharmony_ci [sizeof_cpu_port] "i" (sizeof(cpu_port)), 3638c2ecf20Sopenharmony_ci [cci_enable_req] "i" cpu_to_le32(CCI_ENABLE_REQ), 3648c2ecf20Sopenharmony_ci [cci_control_status_bits] "i" cpu_to_le32(1), 3658c2ecf20Sopenharmony_ci#ifndef __ARMEB__ 3668c2ecf20Sopenharmony_ci [offsetof_cpu_port_mpidr_lsb] "i" (offsetof(struct cpu_port, mpidr)), 3678c2ecf20Sopenharmony_ci#else 3688c2ecf20Sopenharmony_ci [offsetof_cpu_port_mpidr_lsb] "i" (offsetof(struct cpu_port, mpidr)+4), 3698c2ecf20Sopenharmony_ci#endif 3708c2ecf20Sopenharmony_ci [offsetof_cpu_port_port] "i" (offsetof(struct cpu_port, port)), 3718c2ecf20Sopenharmony_ci [sizeof_struct_cpu_port] "i" (sizeof(struct cpu_port)), 3728c2ecf20Sopenharmony_ci [sizeof_struct_ace_port] "i" (sizeof(struct cci_ace_port)), 3738c2ecf20Sopenharmony_ci [offsetof_port_phys] "i" (offsetof(struct cci_ace_port, phys)) ); 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci/** 3778c2ecf20Sopenharmony_ci * __cci_control_port_by_device() - function to control a CCI port by device 3788c2ecf20Sopenharmony_ci * reference 3798c2ecf20Sopenharmony_ci * 3808c2ecf20Sopenharmony_ci * @dn: device node pointer of the device whose CCI port should be 3818c2ecf20Sopenharmony_ci * controlled 3828c2ecf20Sopenharmony_ci * @enable: if true enables the port, if false disables it 3838c2ecf20Sopenharmony_ci * 3848c2ecf20Sopenharmony_ci * Return: 3858c2ecf20Sopenharmony_ci * 0 on success 3868c2ecf20Sopenharmony_ci * -ENODEV on port look-up failure 3878c2ecf20Sopenharmony_ci */ 3888c2ecf20Sopenharmony_ciint notrace __cci_control_port_by_device(struct device_node *dn, bool enable) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci int port; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (!dn) 3938c2ecf20Sopenharmony_ci return -ENODEV; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci port = __cci_ace_get_port(dn, ACE_LITE_PORT); 3968c2ecf20Sopenharmony_ci if (WARN_ONCE(port < 0, "node %pOF ACE lite port look-up failure\n", 3978c2ecf20Sopenharmony_ci dn)) 3988c2ecf20Sopenharmony_ci return -ENODEV; 3998c2ecf20Sopenharmony_ci cci_port_control(port, enable); 4008c2ecf20Sopenharmony_ci return 0; 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__cci_control_port_by_device); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci/** 4058c2ecf20Sopenharmony_ci * __cci_control_port_by_index() - function to control a CCI port by port index 4068c2ecf20Sopenharmony_ci * 4078c2ecf20Sopenharmony_ci * @port: port index previously retrieved with cci_ace_get_port() 4088c2ecf20Sopenharmony_ci * @enable: if true enables the port, if false disables it 4098c2ecf20Sopenharmony_ci * 4108c2ecf20Sopenharmony_ci * Return: 4118c2ecf20Sopenharmony_ci * 0 on success 4128c2ecf20Sopenharmony_ci * -ENODEV on port index out of range 4138c2ecf20Sopenharmony_ci * -EPERM if operation carried out on an ACE PORT 4148c2ecf20Sopenharmony_ci */ 4158c2ecf20Sopenharmony_ciint notrace __cci_control_port_by_index(u32 port, bool enable) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci if (port >= nb_cci_ports || ports[port].type == ACE_INVALID_PORT) 4188c2ecf20Sopenharmony_ci return -ENODEV; 4198c2ecf20Sopenharmony_ci /* 4208c2ecf20Sopenharmony_ci * CCI control for ports connected to CPUS is extremely fragile 4218c2ecf20Sopenharmony_ci * and must be made to go through a specific and controlled 4228c2ecf20Sopenharmony_ci * interface (ie cci_disable_port_by_cpu(); control by general purpose 4238c2ecf20Sopenharmony_ci * indexing is therefore disabled for ACE ports. 4248c2ecf20Sopenharmony_ci */ 4258c2ecf20Sopenharmony_ci if (ports[port].type == ACE_PORT) 4268c2ecf20Sopenharmony_ci return -EPERM; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci cci_port_control(port, enable); 4298c2ecf20Sopenharmony_ci return 0; 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__cci_control_port_by_index); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic const struct of_device_id arm_cci_ctrl_if_matches[] = { 4348c2ecf20Sopenharmony_ci {.compatible = "arm,cci-400-ctrl-if", }, 4358c2ecf20Sopenharmony_ci {}, 4368c2ecf20Sopenharmony_ci}; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic int cci_probe_ports(struct device_node *np) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci struct cci_nb_ports const *cci_config; 4418c2ecf20Sopenharmony_ci int ret, i, nb_ace = 0, nb_ace_lite = 0; 4428c2ecf20Sopenharmony_ci struct device_node *cp; 4438c2ecf20Sopenharmony_ci struct resource res; 4448c2ecf20Sopenharmony_ci const char *match_str; 4458c2ecf20Sopenharmony_ci bool is_ace; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci cci_config = of_match_node(arm_cci_matches, np)->data; 4498c2ecf20Sopenharmony_ci if (!cci_config) 4508c2ecf20Sopenharmony_ci return -ENODEV; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci nb_cci_ports = cci_config->nb_ace + cci_config->nb_ace_lite; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci ports = kcalloc(nb_cci_ports, sizeof(*ports), GFP_KERNEL); 4558c2ecf20Sopenharmony_ci if (!ports) 4568c2ecf20Sopenharmony_ci return -ENOMEM; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci for_each_available_child_of_node(np, cp) { 4598c2ecf20Sopenharmony_ci if (!of_match_node(arm_cci_ctrl_if_matches, cp)) 4608c2ecf20Sopenharmony_ci continue; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci i = nb_ace + nb_ace_lite; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (i >= nb_cci_ports) 4658c2ecf20Sopenharmony_ci break; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (of_property_read_string(cp, "interface-type", 4688c2ecf20Sopenharmony_ci &match_str)) { 4698c2ecf20Sopenharmony_ci WARN(1, "node %pOF missing interface-type property\n", 4708c2ecf20Sopenharmony_ci cp); 4718c2ecf20Sopenharmony_ci continue; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci is_ace = strcmp(match_str, "ace") == 0; 4748c2ecf20Sopenharmony_ci if (!is_ace && strcmp(match_str, "ace-lite")) { 4758c2ecf20Sopenharmony_ci WARN(1, "node %pOF containing invalid interface-type property, skipping it\n", 4768c2ecf20Sopenharmony_ci cp); 4778c2ecf20Sopenharmony_ci continue; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci ret = of_address_to_resource(cp, 0, &res); 4818c2ecf20Sopenharmony_ci if (!ret) { 4828c2ecf20Sopenharmony_ci ports[i].base = ioremap(res.start, resource_size(&res)); 4838c2ecf20Sopenharmony_ci ports[i].phys = res.start; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci if (ret || !ports[i].base) { 4868c2ecf20Sopenharmony_ci WARN(1, "unable to ioremap CCI port %d\n", i); 4878c2ecf20Sopenharmony_ci continue; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (is_ace) { 4918c2ecf20Sopenharmony_ci if (WARN_ON(nb_ace >= cci_config->nb_ace)) 4928c2ecf20Sopenharmony_ci continue; 4938c2ecf20Sopenharmony_ci ports[i].type = ACE_PORT; 4948c2ecf20Sopenharmony_ci ++nb_ace; 4958c2ecf20Sopenharmony_ci } else { 4968c2ecf20Sopenharmony_ci if (WARN_ON(nb_ace_lite >= cci_config->nb_ace_lite)) 4978c2ecf20Sopenharmony_ci continue; 4988c2ecf20Sopenharmony_ci ports[i].type = ACE_LITE_PORT; 4998c2ecf20Sopenharmony_ci ++nb_ace_lite; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci ports[i].dn = cp; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci /* 5058c2ecf20Sopenharmony_ci * If there is no CCI port that is under kernel control 5068c2ecf20Sopenharmony_ci * return early and report probe status. 5078c2ecf20Sopenharmony_ci */ 5088c2ecf20Sopenharmony_ci if (!nb_ace && !nb_ace_lite) 5098c2ecf20Sopenharmony_ci return -ENODEV; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci /* initialize a stashed array of ACE ports to speed-up look-up */ 5128c2ecf20Sopenharmony_ci cci_ace_init_ports(); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci /* 5158c2ecf20Sopenharmony_ci * Multi-cluster systems may need this data when non-coherent, during 5168c2ecf20Sopenharmony_ci * cluster power-up/power-down. Make sure it reaches main memory. 5178c2ecf20Sopenharmony_ci */ 5188c2ecf20Sopenharmony_ci sync_cache_w(&cci_ctrl_base); 5198c2ecf20Sopenharmony_ci sync_cache_w(&cci_ctrl_phys); 5208c2ecf20Sopenharmony_ci sync_cache_w(&ports); 5218c2ecf20Sopenharmony_ci sync_cache_w(&cpu_port); 5228c2ecf20Sopenharmony_ci __sync_cache_range_w(ports, sizeof(*ports) * nb_cci_ports); 5238c2ecf20Sopenharmony_ci pr_info("ARM CCI driver probed\n"); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci return 0; 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci#else /* !CONFIG_ARM_CCI400_PORT_CTRL */ 5288c2ecf20Sopenharmony_cistatic inline int cci_probe_ports(struct device_node *np) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci return 0; 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci#endif /* CONFIG_ARM_CCI400_PORT_CTRL */ 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic int cci_probe(void) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci int ret; 5378c2ecf20Sopenharmony_ci struct device_node *np; 5388c2ecf20Sopenharmony_ci struct resource res; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci np = of_find_matching_node(NULL, arm_cci_matches); 5418c2ecf20Sopenharmony_ci if (!of_device_is_available(np)) 5428c2ecf20Sopenharmony_ci return -ENODEV; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci ret = of_address_to_resource(np, 0, &res); 5458c2ecf20Sopenharmony_ci if (!ret) { 5468c2ecf20Sopenharmony_ci cci_ctrl_base = ioremap(res.start, resource_size(&res)); 5478c2ecf20Sopenharmony_ci cci_ctrl_phys = res.start; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci if (ret || !cci_ctrl_base) { 5508c2ecf20Sopenharmony_ci WARN(1, "unable to ioremap CCI ctrl\n"); 5518c2ecf20Sopenharmony_ci return -ENXIO; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci return cci_probe_ports(np); 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic int cci_init_status = -EAGAIN; 5588c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(cci_probing); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_cistatic int cci_init(void) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci if (cci_init_status != -EAGAIN) 5638c2ecf20Sopenharmony_ci return cci_init_status; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci mutex_lock(&cci_probing); 5668c2ecf20Sopenharmony_ci if (cci_init_status == -EAGAIN) 5678c2ecf20Sopenharmony_ci cci_init_status = cci_probe(); 5688c2ecf20Sopenharmony_ci mutex_unlock(&cci_probing); 5698c2ecf20Sopenharmony_ci return cci_init_status; 5708c2ecf20Sopenharmony_ci} 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci/* 5738c2ecf20Sopenharmony_ci * To sort out early init calls ordering a helper function is provided to 5748c2ecf20Sopenharmony_ci * check if the CCI driver has beed initialized. Function check if the driver 5758c2ecf20Sopenharmony_ci * has been initialized, if not it calls the init function that probes 5768c2ecf20Sopenharmony_ci * the driver and updates the return value. 5778c2ecf20Sopenharmony_ci */ 5788c2ecf20Sopenharmony_cibool cci_probed(void) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci return cci_init() == 0; 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cci_probed); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ciearly_initcall(cci_init); 5858c2ecf20Sopenharmony_cicore_initcall(cci_platform_init); 5868c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 5878c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ARM CCI support"); 588