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