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