162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * CCI cache coherent interconnect driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2013 ARM Ltd. 562306a36Sopenharmony_ci * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 862306a36Sopenharmony_ci * it under the terms of the GNU General Public License version 2 as 962306a36Sopenharmony_ci * published by the Free Software Foundation. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any 1262306a36Sopenharmony_ci * kind, whether express or implied; without even the implied warranty 1362306a36Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1462306a36Sopenharmony_ci * GNU General Public License for more details. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/arm-cci.h> 1862306a36Sopenharmony_ci#include <linux/io.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/of_address.h> 2162306a36Sopenharmony_ci#include <linux/of_platform.h> 2262306a36Sopenharmony_ci#include <linux/platform_device.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <asm/cacheflush.h> 2662306a36Sopenharmony_ci#include <asm/smp_plat.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic void __iomem *cci_ctrl_base __ro_after_init; 2962306a36Sopenharmony_cistatic unsigned long cci_ctrl_phys __ro_after_init; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#ifdef CONFIG_ARM_CCI400_PORT_CTRL 3262306a36Sopenharmony_cistruct cci_nb_ports { 3362306a36Sopenharmony_ci unsigned int nb_ace; 3462306a36Sopenharmony_ci unsigned int nb_ace_lite; 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic const struct cci_nb_ports cci400_ports = { 3862306a36Sopenharmony_ci .nb_ace = 2, 3962306a36Sopenharmony_ci .nb_ace_lite = 3 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define CCI400_PORTS_DATA (&cci400_ports) 4362306a36Sopenharmony_ci#else 4462306a36Sopenharmony_ci#define CCI400_PORTS_DATA (NULL) 4562306a36Sopenharmony_ci#endif 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic const struct of_device_id arm_cci_matches[] = { 4862306a36Sopenharmony_ci#ifdef CONFIG_ARM_CCI400_COMMON 4962306a36Sopenharmony_ci {.compatible = "arm,cci-400", .data = CCI400_PORTS_DATA }, 5062306a36Sopenharmony_ci#endif 5162306a36Sopenharmony_ci#ifdef CONFIG_ARM_CCI5xx_PMU 5262306a36Sopenharmony_ci { .compatible = "arm,cci-500", }, 5362306a36Sopenharmony_ci { .compatible = "arm,cci-550", }, 5462306a36Sopenharmony_ci#endif 5562306a36Sopenharmony_ci {}, 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic const struct of_dev_auxdata arm_cci_auxdata[] = { 5962306a36Sopenharmony_ci OF_DEV_AUXDATA("arm,cci-400-pmu", 0, NULL, &cci_ctrl_base), 6062306a36Sopenharmony_ci OF_DEV_AUXDATA("arm,cci-400-pmu,r0", 0, NULL, &cci_ctrl_base), 6162306a36Sopenharmony_ci OF_DEV_AUXDATA("arm,cci-400-pmu,r1", 0, NULL, &cci_ctrl_base), 6262306a36Sopenharmony_ci OF_DEV_AUXDATA("arm,cci-500-pmu,r0", 0, NULL, &cci_ctrl_base), 6362306a36Sopenharmony_ci OF_DEV_AUXDATA("arm,cci-550-pmu,r0", 0, NULL, &cci_ctrl_base), 6462306a36Sopenharmony_ci {} 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define DRIVER_NAME "ARM-CCI" 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic int cci_platform_probe(struct platform_device *pdev) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci if (!cci_probed()) 7262306a36Sopenharmony_ci return -ENODEV; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci return of_platform_populate(pdev->dev.of_node, NULL, 7562306a36Sopenharmony_ci arm_cci_auxdata, &pdev->dev); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic struct platform_driver cci_platform_driver = { 7962306a36Sopenharmony_ci .driver = { 8062306a36Sopenharmony_ci .name = DRIVER_NAME, 8162306a36Sopenharmony_ci .of_match_table = arm_cci_matches, 8262306a36Sopenharmony_ci }, 8362306a36Sopenharmony_ci .probe = cci_platform_probe, 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int __init cci_platform_init(void) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci return platform_driver_register(&cci_platform_driver); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#ifdef CONFIG_ARM_CCI400_PORT_CTRL 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#define CCI_PORT_CTRL 0x0 9462306a36Sopenharmony_ci#define CCI_CTRL_STATUS 0xc 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#define CCI_ENABLE_SNOOP_REQ 0x1 9762306a36Sopenharmony_ci#define CCI_ENABLE_DVM_REQ 0x2 9862306a36Sopenharmony_ci#define CCI_ENABLE_REQ (CCI_ENABLE_SNOOP_REQ | CCI_ENABLE_DVM_REQ) 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cienum cci_ace_port_type { 10162306a36Sopenharmony_ci ACE_INVALID_PORT = 0x0, 10262306a36Sopenharmony_ci ACE_PORT, 10362306a36Sopenharmony_ci ACE_LITE_PORT, 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistruct cci_ace_port { 10762306a36Sopenharmony_ci void __iomem *base; 10862306a36Sopenharmony_ci unsigned long phys; 10962306a36Sopenharmony_ci enum cci_ace_port_type type; 11062306a36Sopenharmony_ci struct device_node *dn; 11162306a36Sopenharmony_ci}; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic struct cci_ace_port *ports; 11462306a36Sopenharmony_cistatic unsigned int nb_cci_ports; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistruct cpu_port { 11762306a36Sopenharmony_ci u64 mpidr; 11862306a36Sopenharmony_ci u32 port; 11962306a36Sopenharmony_ci}; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* 12262306a36Sopenharmony_ci * Use the port MSB as valid flag, shift can be made dynamic 12362306a36Sopenharmony_ci * by computing number of bits required for port indexes. 12462306a36Sopenharmony_ci * Code disabling CCI cpu ports runs with D-cache invalidated 12562306a36Sopenharmony_ci * and SCTLR bit clear so data accesses must be kept to a minimum 12662306a36Sopenharmony_ci * to improve performance; for now shift is left static to 12762306a36Sopenharmony_ci * avoid one more data access while disabling the CCI port. 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_ci#define PORT_VALID_SHIFT 31 13062306a36Sopenharmony_ci#define PORT_VALID (0x1 << PORT_VALID_SHIFT) 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic inline void init_cpu_port(struct cpu_port *port, u32 index, u64 mpidr) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci port->port = PORT_VALID | index; 13562306a36Sopenharmony_ci port->mpidr = mpidr; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic inline bool cpu_port_is_valid(struct cpu_port *port) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci return !!(port->port & PORT_VALID); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic inline bool cpu_port_match(struct cpu_port *port, u64 mpidr) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci return port->mpidr == (mpidr & MPIDR_HWID_BITMASK); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic struct cpu_port cpu_port[NR_CPUS]; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci/** 15162306a36Sopenharmony_ci * __cci_ace_get_port - Function to retrieve the port index connected to 15262306a36Sopenharmony_ci * a cpu or device. 15362306a36Sopenharmony_ci * 15462306a36Sopenharmony_ci * @dn: device node of the device to look-up 15562306a36Sopenharmony_ci * @type: port type 15662306a36Sopenharmony_ci * 15762306a36Sopenharmony_ci * Return value: 15862306a36Sopenharmony_ci * - CCI port index if success 15962306a36Sopenharmony_ci * - -ENODEV if failure 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_cistatic int __cci_ace_get_port(struct device_node *dn, int type) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci int i; 16462306a36Sopenharmony_ci bool ace_match; 16562306a36Sopenharmony_ci struct device_node *cci_portn; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci cci_portn = of_parse_phandle(dn, "cci-control-port", 0); 16862306a36Sopenharmony_ci for (i = 0; i < nb_cci_ports; i++) { 16962306a36Sopenharmony_ci ace_match = ports[i].type == type; 17062306a36Sopenharmony_ci if (ace_match && cci_portn == ports[i].dn) 17162306a36Sopenharmony_ci return i; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci return -ENODEV; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ciint cci_ace_get_port(struct device_node *dn) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci return __cci_ace_get_port(dn, ACE_LITE_PORT); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cci_ace_get_port); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic void cci_ace_init_ports(void) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci int port, cpu; 18562306a36Sopenharmony_ci struct device_node *cpun; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* 18862306a36Sopenharmony_ci * Port index look-up speeds up the function disabling ports by CPU, 18962306a36Sopenharmony_ci * since the logical to port index mapping is done once and does 19062306a36Sopenharmony_ci * not change after system boot. 19162306a36Sopenharmony_ci * The stashed index array is initialized for all possible CPUs 19262306a36Sopenharmony_ci * at probe time. 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 19562306a36Sopenharmony_ci /* too early to use cpu->of_node */ 19662306a36Sopenharmony_ci cpun = of_get_cpu_node(cpu, NULL); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (WARN(!cpun, "Missing cpu device node\n")) 19962306a36Sopenharmony_ci continue; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci port = __cci_ace_get_port(cpun, ACE_PORT); 20262306a36Sopenharmony_ci if (port < 0) 20362306a36Sopenharmony_ci continue; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci init_cpu_port(&cpu_port[cpu], port, cpu_logical_map(cpu)); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 20962306a36Sopenharmony_ci WARN(!cpu_port_is_valid(&cpu_port[cpu]), 21062306a36Sopenharmony_ci "CPU %u does not have an associated CCI port\n", 21162306a36Sopenharmony_ci cpu); 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci/* 21562306a36Sopenharmony_ci * Functions to enable/disable a CCI interconnect slave port 21662306a36Sopenharmony_ci * 21762306a36Sopenharmony_ci * They are called by low-level power management code to disable slave 21862306a36Sopenharmony_ci * interfaces snoops and DVM broadcast. 21962306a36Sopenharmony_ci * Since they may execute with cache data allocation disabled and 22062306a36Sopenharmony_ci * after the caches have been cleaned and invalidated the functions provide 22162306a36Sopenharmony_ci * no explicit locking since they may run with D-cache disabled, so normal 22262306a36Sopenharmony_ci * cacheable kernel locks based on ldrex/strex may not work. 22362306a36Sopenharmony_ci * Locking has to be provided by BSP implementations to ensure proper 22462306a36Sopenharmony_ci * operations. 22562306a36Sopenharmony_ci */ 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci/** 22862306a36Sopenharmony_ci * cci_port_control() - function to control a CCI port 22962306a36Sopenharmony_ci * 23062306a36Sopenharmony_ci * @port: index of the port to setup 23162306a36Sopenharmony_ci * @enable: if true enables the port, if false disables it 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_cistatic void notrace cci_port_control(unsigned int port, bool enable) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci void __iomem *base = ports[port].base; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci writel_relaxed(enable ? CCI_ENABLE_REQ : 0, base + CCI_PORT_CTRL); 23862306a36Sopenharmony_ci /* 23962306a36Sopenharmony_ci * This function is called from power down procedures 24062306a36Sopenharmony_ci * and must not execute any instruction that might 24162306a36Sopenharmony_ci * cause the processor to be put in a quiescent state 24262306a36Sopenharmony_ci * (eg wfi). Hence, cpu_relax() can not be added to this 24362306a36Sopenharmony_ci * read loop to optimize power, since it might hide possibly 24462306a36Sopenharmony_ci * disruptive operations. 24562306a36Sopenharmony_ci */ 24662306a36Sopenharmony_ci while (readl_relaxed(cci_ctrl_base + CCI_CTRL_STATUS) & 0x1) 24762306a36Sopenharmony_ci ; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci/** 25162306a36Sopenharmony_ci * cci_disable_port_by_cpu() - function to disable a CCI port by CPU 25262306a36Sopenharmony_ci * reference 25362306a36Sopenharmony_ci * 25462306a36Sopenharmony_ci * @mpidr: mpidr of the CPU whose CCI port should be disabled 25562306a36Sopenharmony_ci * 25662306a36Sopenharmony_ci * Disabling a CCI port for a CPU implies disabling the CCI port 25762306a36Sopenharmony_ci * controlling that CPU cluster. Code disabling CPU CCI ports 25862306a36Sopenharmony_ci * must make sure that the CPU running the code is the last active CPU 25962306a36Sopenharmony_ci * in the cluster ie all other CPUs are quiescent in a low power state. 26062306a36Sopenharmony_ci * 26162306a36Sopenharmony_ci * Return: 26262306a36Sopenharmony_ci * 0 on success 26362306a36Sopenharmony_ci * -ENODEV on port look-up failure 26462306a36Sopenharmony_ci */ 26562306a36Sopenharmony_ciint notrace cci_disable_port_by_cpu(u64 mpidr) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci int cpu; 26862306a36Sopenharmony_ci bool is_valid; 26962306a36Sopenharmony_ci for (cpu = 0; cpu < nr_cpu_ids; cpu++) { 27062306a36Sopenharmony_ci is_valid = cpu_port_is_valid(&cpu_port[cpu]); 27162306a36Sopenharmony_ci if (is_valid && cpu_port_match(&cpu_port[cpu], mpidr)) { 27262306a36Sopenharmony_ci cci_port_control(cpu_port[cpu].port, false); 27362306a36Sopenharmony_ci return 0; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci return -ENODEV; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cci_disable_port_by_cpu); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci/** 28162306a36Sopenharmony_ci * cci_enable_port_for_self() - enable a CCI port for calling CPU 28262306a36Sopenharmony_ci * 28362306a36Sopenharmony_ci * Enabling a CCI port for the calling CPU implies enabling the CCI 28462306a36Sopenharmony_ci * port controlling that CPU's cluster. Caller must make sure that the 28562306a36Sopenharmony_ci * CPU running the code is the first active CPU in the cluster and all 28662306a36Sopenharmony_ci * other CPUs are quiescent in a low power state or waiting for this CPU 28762306a36Sopenharmony_ci * to complete the CCI initialization. 28862306a36Sopenharmony_ci * 28962306a36Sopenharmony_ci * Because this is called when the MMU is still off and with no stack, 29062306a36Sopenharmony_ci * the code must be position independent and ideally rely on callee 29162306a36Sopenharmony_ci * clobbered registers only. To achieve this we must code this function 29262306a36Sopenharmony_ci * entirely in assembler. 29362306a36Sopenharmony_ci * 29462306a36Sopenharmony_ci * On success this returns with the proper CCI port enabled. In case of 29562306a36Sopenharmony_ci * any failure this never returns as the inability to enable the CCI is 29662306a36Sopenharmony_ci * fatal and there is no possible recovery at this stage. 29762306a36Sopenharmony_ci */ 29862306a36Sopenharmony_ciasmlinkage void __naked cci_enable_port_for_self(void) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci asm volatile ("\n" 30162306a36Sopenharmony_ci" .arch armv7-a\n" 30262306a36Sopenharmony_ci" mrc p15, 0, r0, c0, c0, 5 @ get MPIDR value \n" 30362306a36Sopenharmony_ci" and r0, r0, #"__stringify(MPIDR_HWID_BITMASK)" \n" 30462306a36Sopenharmony_ci" adr r1, 5f \n" 30562306a36Sopenharmony_ci" ldr r2, [r1] \n" 30662306a36Sopenharmony_ci" add r1, r1, r2 @ &cpu_port \n" 30762306a36Sopenharmony_ci" add ip, r1, %[sizeof_cpu_port] \n" 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* Loop over the cpu_port array looking for a matching MPIDR */ 31062306a36Sopenharmony_ci"1: ldr r2, [r1, %[offsetof_cpu_port_mpidr_lsb]] \n" 31162306a36Sopenharmony_ci" cmp r2, r0 @ compare MPIDR \n" 31262306a36Sopenharmony_ci" bne 2f \n" 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* Found a match, now test port validity */ 31562306a36Sopenharmony_ci" ldr r3, [r1, %[offsetof_cpu_port_port]] \n" 31662306a36Sopenharmony_ci" tst r3, #"__stringify(PORT_VALID)" \n" 31762306a36Sopenharmony_ci" bne 3f \n" 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* no match, loop with the next cpu_port entry */ 32062306a36Sopenharmony_ci"2: add r1, r1, %[sizeof_struct_cpu_port] \n" 32162306a36Sopenharmony_ci" cmp r1, ip @ done? \n" 32262306a36Sopenharmony_ci" blo 1b \n" 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* CCI port not found -- cheaply try to stall this CPU */ 32562306a36Sopenharmony_ci"cci_port_not_found: \n" 32662306a36Sopenharmony_ci" wfi \n" 32762306a36Sopenharmony_ci" wfe \n" 32862306a36Sopenharmony_ci" b cci_port_not_found \n" 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* Use matched port index to look up the corresponding ports entry */ 33162306a36Sopenharmony_ci"3: bic r3, r3, #"__stringify(PORT_VALID)" \n" 33262306a36Sopenharmony_ci" adr r0, 6f \n" 33362306a36Sopenharmony_ci" ldmia r0, {r1, r2} \n" 33462306a36Sopenharmony_ci" sub r1, r1, r0 @ virt - phys \n" 33562306a36Sopenharmony_ci" ldr r0, [r0, r2] @ *(&ports) \n" 33662306a36Sopenharmony_ci" mov r2, %[sizeof_struct_ace_port] \n" 33762306a36Sopenharmony_ci" mla r0, r2, r3, r0 @ &ports[index] \n" 33862306a36Sopenharmony_ci" sub r0, r0, r1 @ virt_to_phys() \n" 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* Enable the CCI port */ 34162306a36Sopenharmony_ci" ldr r0, [r0, %[offsetof_port_phys]] \n" 34262306a36Sopenharmony_ci" mov r3, %[cci_enable_req]\n" 34362306a36Sopenharmony_ci" str r3, [r0, #"__stringify(CCI_PORT_CTRL)"] \n" 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* poll the status reg for completion */ 34662306a36Sopenharmony_ci" adr r1, 7f \n" 34762306a36Sopenharmony_ci" ldr r0, [r1] \n" 34862306a36Sopenharmony_ci" ldr r0, [r0, r1] @ cci_ctrl_base \n" 34962306a36Sopenharmony_ci"4: ldr r1, [r0, #"__stringify(CCI_CTRL_STATUS)"] \n" 35062306a36Sopenharmony_ci" tst r1, %[cci_control_status_bits] \n" 35162306a36Sopenharmony_ci" bne 4b \n" 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci" mov r0, #0 \n" 35462306a36Sopenharmony_ci" bx lr \n" 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci" .align 2 \n" 35762306a36Sopenharmony_ci"5: .word cpu_port - . \n" 35862306a36Sopenharmony_ci"6: .word . \n" 35962306a36Sopenharmony_ci" .word ports - 6b \n" 36062306a36Sopenharmony_ci"7: .word cci_ctrl_phys - . \n" 36162306a36Sopenharmony_ci : : 36262306a36Sopenharmony_ci [sizeof_cpu_port] "i" (sizeof(cpu_port)), 36362306a36Sopenharmony_ci [cci_enable_req] "i" cpu_to_le32(CCI_ENABLE_REQ), 36462306a36Sopenharmony_ci [cci_control_status_bits] "i" cpu_to_le32(1), 36562306a36Sopenharmony_ci#ifndef __ARMEB__ 36662306a36Sopenharmony_ci [offsetof_cpu_port_mpidr_lsb] "i" (offsetof(struct cpu_port, mpidr)), 36762306a36Sopenharmony_ci#else 36862306a36Sopenharmony_ci [offsetof_cpu_port_mpidr_lsb] "i" (offsetof(struct cpu_port, mpidr)+4), 36962306a36Sopenharmony_ci#endif 37062306a36Sopenharmony_ci [offsetof_cpu_port_port] "i" (offsetof(struct cpu_port, port)), 37162306a36Sopenharmony_ci [sizeof_struct_cpu_port] "i" (sizeof(struct cpu_port)), 37262306a36Sopenharmony_ci [sizeof_struct_ace_port] "i" (sizeof(struct cci_ace_port)), 37362306a36Sopenharmony_ci [offsetof_port_phys] "i" (offsetof(struct cci_ace_port, phys)) ); 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci/** 37762306a36Sopenharmony_ci * __cci_control_port_by_device() - function to control a CCI port by device 37862306a36Sopenharmony_ci * reference 37962306a36Sopenharmony_ci * 38062306a36Sopenharmony_ci * @dn: device node pointer of the device whose CCI port should be 38162306a36Sopenharmony_ci * controlled 38262306a36Sopenharmony_ci * @enable: if true enables the port, if false disables it 38362306a36Sopenharmony_ci * 38462306a36Sopenharmony_ci * Return: 38562306a36Sopenharmony_ci * 0 on success 38662306a36Sopenharmony_ci * -ENODEV on port look-up failure 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_ciint notrace __cci_control_port_by_device(struct device_node *dn, bool enable) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci int port; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if (!dn) 39362306a36Sopenharmony_ci return -ENODEV; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci port = __cci_ace_get_port(dn, ACE_LITE_PORT); 39662306a36Sopenharmony_ci if (WARN_ONCE(port < 0, "node %pOF ACE lite port look-up failure\n", 39762306a36Sopenharmony_ci dn)) 39862306a36Sopenharmony_ci return -ENODEV; 39962306a36Sopenharmony_ci cci_port_control(port, enable); 40062306a36Sopenharmony_ci return 0; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__cci_control_port_by_device); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci/** 40562306a36Sopenharmony_ci * __cci_control_port_by_index() - function to control a CCI port by port index 40662306a36Sopenharmony_ci * 40762306a36Sopenharmony_ci * @port: port index previously retrieved with cci_ace_get_port() 40862306a36Sopenharmony_ci * @enable: if true enables the port, if false disables it 40962306a36Sopenharmony_ci * 41062306a36Sopenharmony_ci * Return: 41162306a36Sopenharmony_ci * 0 on success 41262306a36Sopenharmony_ci * -ENODEV on port index out of range 41362306a36Sopenharmony_ci * -EPERM if operation carried out on an ACE PORT 41462306a36Sopenharmony_ci */ 41562306a36Sopenharmony_ciint notrace __cci_control_port_by_index(u32 port, bool enable) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci if (port >= nb_cci_ports || ports[port].type == ACE_INVALID_PORT) 41862306a36Sopenharmony_ci return -ENODEV; 41962306a36Sopenharmony_ci /* 42062306a36Sopenharmony_ci * CCI control for ports connected to CPUS is extremely fragile 42162306a36Sopenharmony_ci * and must be made to go through a specific and controlled 42262306a36Sopenharmony_ci * interface (ie cci_disable_port_by_cpu(); control by general purpose 42362306a36Sopenharmony_ci * indexing is therefore disabled for ACE ports. 42462306a36Sopenharmony_ci */ 42562306a36Sopenharmony_ci if (ports[port].type == ACE_PORT) 42662306a36Sopenharmony_ci return -EPERM; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci cci_port_control(port, enable); 42962306a36Sopenharmony_ci return 0; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__cci_control_port_by_index); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic const struct of_device_id arm_cci_ctrl_if_matches[] = { 43462306a36Sopenharmony_ci {.compatible = "arm,cci-400-ctrl-if", }, 43562306a36Sopenharmony_ci {}, 43662306a36Sopenharmony_ci}; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cistatic int cci_probe_ports(struct device_node *np) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci struct cci_nb_ports const *cci_config; 44162306a36Sopenharmony_ci int ret, i, nb_ace = 0, nb_ace_lite = 0; 44262306a36Sopenharmony_ci struct device_node *cp; 44362306a36Sopenharmony_ci struct resource res; 44462306a36Sopenharmony_ci const char *match_str; 44562306a36Sopenharmony_ci bool is_ace; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci cci_config = of_match_node(arm_cci_matches, np)->data; 44962306a36Sopenharmony_ci if (!cci_config) 45062306a36Sopenharmony_ci return -ENODEV; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci nb_cci_ports = cci_config->nb_ace + cci_config->nb_ace_lite; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci ports = kcalloc(nb_cci_ports, sizeof(*ports), GFP_KERNEL); 45562306a36Sopenharmony_ci if (!ports) 45662306a36Sopenharmony_ci return -ENOMEM; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci for_each_available_child_of_node(np, cp) { 45962306a36Sopenharmony_ci if (!of_match_node(arm_cci_ctrl_if_matches, cp)) 46062306a36Sopenharmony_ci continue; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci i = nb_ace + nb_ace_lite; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (i >= nb_cci_ports) 46562306a36Sopenharmony_ci break; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (of_property_read_string(cp, "interface-type", 46862306a36Sopenharmony_ci &match_str)) { 46962306a36Sopenharmony_ci WARN(1, "node %pOF missing interface-type property\n", 47062306a36Sopenharmony_ci cp); 47162306a36Sopenharmony_ci continue; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci is_ace = strcmp(match_str, "ace") == 0; 47462306a36Sopenharmony_ci if (!is_ace && strcmp(match_str, "ace-lite")) { 47562306a36Sopenharmony_ci WARN(1, "node %pOF containing invalid interface-type property, skipping it\n", 47662306a36Sopenharmony_ci cp); 47762306a36Sopenharmony_ci continue; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci ret = of_address_to_resource(cp, 0, &res); 48162306a36Sopenharmony_ci if (!ret) { 48262306a36Sopenharmony_ci ports[i].base = ioremap(res.start, resource_size(&res)); 48362306a36Sopenharmony_ci ports[i].phys = res.start; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci if (ret || !ports[i].base) { 48662306a36Sopenharmony_ci WARN(1, "unable to ioremap CCI port %d\n", i); 48762306a36Sopenharmony_ci continue; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (is_ace) { 49162306a36Sopenharmony_ci if (WARN_ON(nb_ace >= cci_config->nb_ace)) 49262306a36Sopenharmony_ci continue; 49362306a36Sopenharmony_ci ports[i].type = ACE_PORT; 49462306a36Sopenharmony_ci ++nb_ace; 49562306a36Sopenharmony_ci } else { 49662306a36Sopenharmony_ci if (WARN_ON(nb_ace_lite >= cci_config->nb_ace_lite)) 49762306a36Sopenharmony_ci continue; 49862306a36Sopenharmony_ci ports[i].type = ACE_LITE_PORT; 49962306a36Sopenharmony_ci ++nb_ace_lite; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci ports[i].dn = cp; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci /* 50562306a36Sopenharmony_ci * If there is no CCI port that is under kernel control 50662306a36Sopenharmony_ci * return early and report probe status. 50762306a36Sopenharmony_ci */ 50862306a36Sopenharmony_ci if (!nb_ace && !nb_ace_lite) 50962306a36Sopenharmony_ci return -ENODEV; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci /* initialize a stashed array of ACE ports to speed-up look-up */ 51262306a36Sopenharmony_ci cci_ace_init_ports(); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* 51562306a36Sopenharmony_ci * Multi-cluster systems may need this data when non-coherent, during 51662306a36Sopenharmony_ci * cluster power-up/power-down. Make sure it reaches main memory. 51762306a36Sopenharmony_ci */ 51862306a36Sopenharmony_ci sync_cache_w(&cci_ctrl_base); 51962306a36Sopenharmony_ci sync_cache_w(&cci_ctrl_phys); 52062306a36Sopenharmony_ci sync_cache_w(&ports); 52162306a36Sopenharmony_ci sync_cache_w(&cpu_port); 52262306a36Sopenharmony_ci __sync_cache_range_w(ports, sizeof(*ports) * nb_cci_ports); 52362306a36Sopenharmony_ci pr_info("ARM CCI driver probed\n"); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci return 0; 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci#else /* !CONFIG_ARM_CCI400_PORT_CTRL */ 52862306a36Sopenharmony_cistatic inline int cci_probe_ports(struct device_node *np) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci return 0; 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci#endif /* CONFIG_ARM_CCI400_PORT_CTRL */ 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic int cci_probe(void) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci int ret; 53762306a36Sopenharmony_ci struct device_node *np; 53862306a36Sopenharmony_ci struct resource res; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci np = of_find_matching_node(NULL, arm_cci_matches); 54162306a36Sopenharmony_ci if (!of_device_is_available(np)) 54262306a36Sopenharmony_ci return -ENODEV; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci ret = of_address_to_resource(np, 0, &res); 54562306a36Sopenharmony_ci if (!ret) { 54662306a36Sopenharmony_ci cci_ctrl_base = ioremap(res.start, resource_size(&res)); 54762306a36Sopenharmony_ci cci_ctrl_phys = res.start; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci if (ret || !cci_ctrl_base) { 55062306a36Sopenharmony_ci WARN(1, "unable to ioremap CCI ctrl\n"); 55162306a36Sopenharmony_ci return -ENXIO; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci return cci_probe_ports(np); 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cistatic int cci_init_status = -EAGAIN; 55862306a36Sopenharmony_cistatic DEFINE_MUTEX(cci_probing); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cistatic int cci_init(void) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci if (cci_init_status != -EAGAIN) 56362306a36Sopenharmony_ci return cci_init_status; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci mutex_lock(&cci_probing); 56662306a36Sopenharmony_ci if (cci_init_status == -EAGAIN) 56762306a36Sopenharmony_ci cci_init_status = cci_probe(); 56862306a36Sopenharmony_ci mutex_unlock(&cci_probing); 56962306a36Sopenharmony_ci return cci_init_status; 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci/* 57362306a36Sopenharmony_ci * To sort out early init calls ordering a helper function is provided to 57462306a36Sopenharmony_ci * check if the CCI driver has beed initialized. Function check if the driver 57562306a36Sopenharmony_ci * has been initialized, if not it calls the init function that probes 57662306a36Sopenharmony_ci * the driver and updates the return value. 57762306a36Sopenharmony_ci */ 57862306a36Sopenharmony_cibool cci_probed(void) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci return cci_init() == 0; 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cci_probed); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ciearly_initcall(cci_init); 58562306a36Sopenharmony_cicore_initcall(cci_platform_init); 58662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 58762306a36Sopenharmony_ciMODULE_DESCRIPTION("ARM CCI support"); 588