162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * (c) Copyright 2006 Benjamin Herrenschmidt, IBM Corp.
462306a36Sopenharmony_ci *                    <benh@kernel.crashing.org>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#undef DEBUG
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/export.h>
1162306a36Sopenharmony_ci#include <linux/of_address.h>
1262306a36Sopenharmony_ci#include <asm/dcr.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#ifdef CONFIG_PPC_DCR_MMIO
1562306a36Sopenharmony_cistatic struct device_node *find_dcr_parent(struct device_node *node)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	struct device_node *par, *tmp;
1862306a36Sopenharmony_ci	const u32 *p;
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	for (par = of_node_get(node); par;) {
2162306a36Sopenharmony_ci		if (of_property_read_bool(par, "dcr-controller"))
2262306a36Sopenharmony_ci			break;
2362306a36Sopenharmony_ci		p = of_get_property(par, "dcr-parent", NULL);
2462306a36Sopenharmony_ci		tmp = par;
2562306a36Sopenharmony_ci		if (p == NULL)
2662306a36Sopenharmony_ci			par = of_get_parent(par);
2762306a36Sopenharmony_ci		else
2862306a36Sopenharmony_ci			par = of_find_node_by_phandle(*p);
2962306a36Sopenharmony_ci		of_node_put(tmp);
3062306a36Sopenharmony_ci	}
3162306a36Sopenharmony_ci	return par;
3262306a36Sopenharmony_ci}
3362306a36Sopenharmony_ci#endif
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#if defined(CONFIG_PPC_DCR_NATIVE) && defined(CONFIG_PPC_DCR_MMIO)
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cibool dcr_map_ok_generic(dcr_host_t host)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	if (host.type == DCR_HOST_NATIVE)
4062306a36Sopenharmony_ci		return dcr_map_ok_native(host.host.native);
4162306a36Sopenharmony_ci	else if (host.type == DCR_HOST_MMIO)
4262306a36Sopenharmony_ci		return dcr_map_ok_mmio(host.host.mmio);
4362306a36Sopenharmony_ci	else
4462306a36Sopenharmony_ci		return false;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dcr_map_ok_generic);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cidcr_host_t dcr_map_generic(struct device_node *dev,
4962306a36Sopenharmony_ci			   unsigned int dcr_n,
5062306a36Sopenharmony_ci			   unsigned int dcr_c)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	dcr_host_t host;
5362306a36Sopenharmony_ci	struct device_node *dp;
5462306a36Sopenharmony_ci	const char *prop;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	host.type = DCR_HOST_INVALID;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	dp = find_dcr_parent(dev);
5962306a36Sopenharmony_ci	if (dp == NULL)
6062306a36Sopenharmony_ci		return host;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	prop = of_get_property(dp, "dcr-access-method", NULL);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	pr_debug("dcr_map_generic(dcr-access-method = %s)\n", prop);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	if (!strcmp(prop, "native")) {
6762306a36Sopenharmony_ci		host.type = DCR_HOST_NATIVE;
6862306a36Sopenharmony_ci		host.host.native = dcr_map_native(dev, dcr_n, dcr_c);
6962306a36Sopenharmony_ci	} else if (!strcmp(prop, "mmio")) {
7062306a36Sopenharmony_ci		host.type = DCR_HOST_MMIO;
7162306a36Sopenharmony_ci		host.host.mmio = dcr_map_mmio(dev, dcr_n, dcr_c);
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	of_node_put(dp);
7562306a36Sopenharmony_ci	return host;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dcr_map_generic);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_civoid dcr_unmap_generic(dcr_host_t host, unsigned int dcr_c)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	if (host.type == DCR_HOST_NATIVE)
8262306a36Sopenharmony_ci		dcr_unmap_native(host.host.native, dcr_c);
8362306a36Sopenharmony_ci	else if (host.type == DCR_HOST_MMIO)
8462306a36Sopenharmony_ci		dcr_unmap_mmio(host.host.mmio, dcr_c);
8562306a36Sopenharmony_ci	else /* host.type == DCR_HOST_INVALID */
8662306a36Sopenharmony_ci		WARN_ON(true);
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dcr_unmap_generic);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ciu32 dcr_read_generic(dcr_host_t host, unsigned int dcr_n)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	if (host.type == DCR_HOST_NATIVE)
9362306a36Sopenharmony_ci		return dcr_read_native(host.host.native, dcr_n);
9462306a36Sopenharmony_ci	else if (host.type == DCR_HOST_MMIO)
9562306a36Sopenharmony_ci		return dcr_read_mmio(host.host.mmio, dcr_n);
9662306a36Sopenharmony_ci	else /* host.type == DCR_HOST_INVALID */
9762306a36Sopenharmony_ci		WARN_ON(true);
9862306a36Sopenharmony_ci	return 0;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dcr_read_generic);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_civoid dcr_write_generic(dcr_host_t host, unsigned int dcr_n, u32 value)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	if (host.type == DCR_HOST_NATIVE)
10562306a36Sopenharmony_ci		dcr_write_native(host.host.native, dcr_n, value);
10662306a36Sopenharmony_ci	else if (host.type == DCR_HOST_MMIO)
10762306a36Sopenharmony_ci		dcr_write_mmio(host.host.mmio, dcr_n, value);
10862306a36Sopenharmony_ci	else /* host.type == DCR_HOST_INVALID */
10962306a36Sopenharmony_ci		WARN_ON(true);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dcr_write_generic);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci#endif /* defined(CONFIG_PPC_DCR_NATIVE) && defined(CONFIG_PPC_DCR_MMIO) */
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ciunsigned int dcr_resource_start(const struct device_node *np,
11662306a36Sopenharmony_ci				unsigned int index)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	unsigned int ds;
11962306a36Sopenharmony_ci	const u32 *dr = of_get_property(np, "dcr-reg", &ds);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (dr == NULL || ds & 1 || index >= (ds / 8))
12262306a36Sopenharmony_ci		return 0;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	return dr[index * 2];
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dcr_resource_start);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ciunsigned int dcr_resource_len(const struct device_node *np, unsigned int index)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	unsigned int ds;
13162306a36Sopenharmony_ci	const u32 *dr = of_get_property(np, "dcr-reg", &ds);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (dr == NULL || ds & 1 || index >= (ds / 8))
13462306a36Sopenharmony_ci		return 0;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	return dr[index * 2 + 1];
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dcr_resource_len);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci#ifdef CONFIG_PPC_DCR_MMIO
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic u64 of_translate_dcr_address(struct device_node *dev,
14362306a36Sopenharmony_ci				    unsigned int dcr_n,
14462306a36Sopenharmony_ci				    unsigned int *out_stride)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct device_node *dp;
14762306a36Sopenharmony_ci	const u32 *p;
14862306a36Sopenharmony_ci	unsigned int stride;
14962306a36Sopenharmony_ci	u64 ret = OF_BAD_ADDR;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	dp = find_dcr_parent(dev);
15262306a36Sopenharmony_ci	if (dp == NULL)
15362306a36Sopenharmony_ci		return OF_BAD_ADDR;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	/* Stride is not properly defined yet, default to 0x10 for Axon */
15662306a36Sopenharmony_ci	p = of_get_property(dp, "dcr-mmio-stride", NULL);
15762306a36Sopenharmony_ci	stride = (p == NULL) ? 0x10 : *p;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	/* XXX FIXME: Which property name is to use of the 2 following ? */
16062306a36Sopenharmony_ci	p = of_get_property(dp, "dcr-mmio-range", NULL);
16162306a36Sopenharmony_ci	if (p == NULL)
16262306a36Sopenharmony_ci		p = of_get_property(dp, "dcr-mmio-space", NULL);
16362306a36Sopenharmony_ci	if (p == NULL)
16462306a36Sopenharmony_ci		goto done;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	/* Maybe could do some better range checking here */
16762306a36Sopenharmony_ci	ret = of_translate_address(dp, p);
16862306a36Sopenharmony_ci	if (ret != OF_BAD_ADDR)
16962306a36Sopenharmony_ci		ret += (u64)(stride) * (u64)dcr_n;
17062306a36Sopenharmony_ci	if (out_stride)
17162306a36Sopenharmony_ci		*out_stride = stride;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci done:
17462306a36Sopenharmony_ci	of_node_put(dp);
17562306a36Sopenharmony_ci	return ret;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cidcr_host_mmio_t dcr_map_mmio(struct device_node *dev,
17962306a36Sopenharmony_ci			     unsigned int dcr_n,
18062306a36Sopenharmony_ci			     unsigned int dcr_c)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	dcr_host_mmio_t ret = { .token = NULL, .stride = 0, .base = dcr_n };
18362306a36Sopenharmony_ci	u64 addr;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	pr_debug("dcr_map(%pOF, 0x%x, 0x%x)\n",
18662306a36Sopenharmony_ci		 dev, dcr_n, dcr_c);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	addr = of_translate_dcr_address(dev, dcr_n, &ret.stride);
18962306a36Sopenharmony_ci	pr_debug("translates to addr: 0x%llx, stride: 0x%x\n",
19062306a36Sopenharmony_ci		 (unsigned long long) addr, ret.stride);
19162306a36Sopenharmony_ci	if (addr == OF_BAD_ADDR)
19262306a36Sopenharmony_ci		return ret;
19362306a36Sopenharmony_ci	pr_debug("mapping 0x%x bytes\n", dcr_c * ret.stride);
19462306a36Sopenharmony_ci	ret.token = ioremap(addr, dcr_c * ret.stride);
19562306a36Sopenharmony_ci	if (ret.token == NULL)
19662306a36Sopenharmony_ci		return ret;
19762306a36Sopenharmony_ci	pr_debug("mapped at 0x%p -> base is 0x%p\n",
19862306a36Sopenharmony_ci		 ret.token, ret.token - dcr_n * ret.stride);
19962306a36Sopenharmony_ci	ret.token -= dcr_n * ret.stride;
20062306a36Sopenharmony_ci	return ret;
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dcr_map_mmio);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_civoid dcr_unmap_mmio(dcr_host_mmio_t host, unsigned int dcr_c)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	dcr_host_mmio_t h = host;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (h.token == NULL)
20962306a36Sopenharmony_ci		return;
21062306a36Sopenharmony_ci	h.token += host.base * h.stride;
21162306a36Sopenharmony_ci	iounmap(h.token);
21262306a36Sopenharmony_ci	h.token = NULL;
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dcr_unmap_mmio);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci#endif /* defined(CONFIG_PPC_DCR_MMIO) */
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci#ifdef CONFIG_PPC_DCR_NATIVE
21962306a36Sopenharmony_ciDEFINE_SPINLOCK(dcr_ind_lock);
22062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dcr_ind_lock);
22162306a36Sopenharmony_ci#endif	/* defined(CONFIG_PPC_DCR_NATIVE) */
22262306a36Sopenharmony_ci
223