11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
24c75a6f4SBenjamin Herrenschmidt /*
34c75a6f4SBenjamin Herrenschmidt * (c) Copyright 2006 Benjamin Herrenschmidt, IBM Corp.
44c75a6f4SBenjamin Herrenschmidt * <benh@kernel.crashing.org>
54c75a6f4SBenjamin Herrenschmidt */
64c75a6f4SBenjamin Herrenschmidt
74c75a6f4SBenjamin Herrenschmidt #undef DEBUG
84c75a6f4SBenjamin Herrenschmidt
94c75a6f4SBenjamin Herrenschmidt #include <linux/kernel.h>
1066b15db6SPaul Gortmaker #include <linux/export.h>
11e6f6390aSChristophe Leroy #include <linux/of_address.h>
124c75a6f4SBenjamin Herrenschmidt #include <asm/dcr.h>
134c75a6f4SBenjamin Herrenschmidt
140723abd0SJosh Boyer #ifdef CONFIG_PPC_DCR_MMIO
find_dcr_parent(struct device_node * node)15b786af11SStephen Neuendorffer static struct device_node *find_dcr_parent(struct device_node *node)
16b786af11SStephen Neuendorffer {
17b786af11SStephen Neuendorffer struct device_node *par, *tmp;
18b786af11SStephen Neuendorffer const u32 *p;
19b786af11SStephen Neuendorffer
20b786af11SStephen Neuendorffer for (par = of_node_get(node); par;) {
21*4d57e351SRob Herring if (of_property_read_bool(par, "dcr-controller"))
22b786af11SStephen Neuendorffer break;
23b786af11SStephen Neuendorffer p = of_get_property(par, "dcr-parent", NULL);
24b786af11SStephen Neuendorffer tmp = par;
25b786af11SStephen Neuendorffer if (p == NULL)
26b786af11SStephen Neuendorffer par = of_get_parent(par);
27b786af11SStephen Neuendorffer else
28b786af11SStephen Neuendorffer par = of_find_node_by_phandle(*p);
29b786af11SStephen Neuendorffer of_node_put(tmp);
30b786af11SStephen Neuendorffer }
31b786af11SStephen Neuendorffer return par;
32b786af11SStephen Neuendorffer }
330723abd0SJosh Boyer #endif
34b786af11SStephen Neuendorffer
35b786af11SStephen Neuendorffer #if defined(CONFIG_PPC_DCR_NATIVE) && defined(CONFIG_PPC_DCR_MMIO)
36b786af11SStephen Neuendorffer
dcr_map_ok_generic(dcr_host_t host)37b786af11SStephen Neuendorffer bool dcr_map_ok_generic(dcr_host_t host)
38b786af11SStephen Neuendorffer {
39b786af11SStephen Neuendorffer if (host.type == DCR_HOST_NATIVE)
40b786af11SStephen Neuendorffer return dcr_map_ok_native(host.host.native);
41b786af11SStephen Neuendorffer else if (host.type == DCR_HOST_MMIO)
42b786af11SStephen Neuendorffer return dcr_map_ok_mmio(host.host.mmio);
43b786af11SStephen Neuendorffer else
44acdb6685SJoe Perches return false;
45b786af11SStephen Neuendorffer }
46b786af11SStephen Neuendorffer EXPORT_SYMBOL_GPL(dcr_map_ok_generic);
47b786af11SStephen Neuendorffer
dcr_map_generic(struct device_node * dev,unsigned int dcr_n,unsigned int dcr_c)48b786af11SStephen Neuendorffer dcr_host_t dcr_map_generic(struct device_node *dev,
49b786af11SStephen Neuendorffer unsigned int dcr_n,
50b786af11SStephen Neuendorffer unsigned int dcr_c)
51b786af11SStephen Neuendorffer {
52b786af11SStephen Neuendorffer dcr_host_t host;
53b786af11SStephen Neuendorffer struct device_node *dp;
54b786af11SStephen Neuendorffer const char *prop;
55b786af11SStephen Neuendorffer
56b786af11SStephen Neuendorffer host.type = DCR_HOST_INVALID;
57b786af11SStephen Neuendorffer
58b786af11SStephen Neuendorffer dp = find_dcr_parent(dev);
59b786af11SStephen Neuendorffer if (dp == NULL)
60b786af11SStephen Neuendorffer return host;
61b786af11SStephen Neuendorffer
62b786af11SStephen Neuendorffer prop = of_get_property(dp, "dcr-access-method", NULL);
63b786af11SStephen Neuendorffer
64b786af11SStephen Neuendorffer pr_debug("dcr_map_generic(dcr-access-method = %s)\n", prop);
65b786af11SStephen Neuendorffer
66b786af11SStephen Neuendorffer if (!strcmp(prop, "native")) {
67b786af11SStephen Neuendorffer host.type = DCR_HOST_NATIVE;
68b786af11SStephen Neuendorffer host.host.native = dcr_map_native(dev, dcr_n, dcr_c);
69b786af11SStephen Neuendorffer } else if (!strcmp(prop, "mmio")) {
70b786af11SStephen Neuendorffer host.type = DCR_HOST_MMIO;
71b786af11SStephen Neuendorffer host.host.mmio = dcr_map_mmio(dev, dcr_n, dcr_c);
72b786af11SStephen Neuendorffer }
73b786af11SStephen Neuendorffer
74b786af11SStephen Neuendorffer of_node_put(dp);
75b786af11SStephen Neuendorffer return host;
76b786af11SStephen Neuendorffer }
77b786af11SStephen Neuendorffer EXPORT_SYMBOL_GPL(dcr_map_generic);
78b786af11SStephen Neuendorffer
dcr_unmap_generic(dcr_host_t host,unsigned int dcr_c)79b786af11SStephen Neuendorffer void dcr_unmap_generic(dcr_host_t host, unsigned int dcr_c)
80b786af11SStephen Neuendorffer {
81b786af11SStephen Neuendorffer if (host.type == DCR_HOST_NATIVE)
82b786af11SStephen Neuendorffer dcr_unmap_native(host.host.native, dcr_c);
83b786af11SStephen Neuendorffer else if (host.type == DCR_HOST_MMIO)
84b786af11SStephen Neuendorffer dcr_unmap_mmio(host.host.mmio, dcr_c);
85b786af11SStephen Neuendorffer else /* host.type == DCR_HOST_INVALID */
86b786af11SStephen Neuendorffer WARN_ON(true);
87b786af11SStephen Neuendorffer }
88b786af11SStephen Neuendorffer EXPORT_SYMBOL_GPL(dcr_unmap_generic);
89b786af11SStephen Neuendorffer
dcr_read_generic(dcr_host_t host,unsigned int dcr_n)90b786af11SStephen Neuendorffer u32 dcr_read_generic(dcr_host_t host, unsigned int dcr_n)
91b786af11SStephen Neuendorffer {
92b786af11SStephen Neuendorffer if (host.type == DCR_HOST_NATIVE)
93b786af11SStephen Neuendorffer return dcr_read_native(host.host.native, dcr_n);
94b786af11SStephen Neuendorffer else if (host.type == DCR_HOST_MMIO)
95b786af11SStephen Neuendorffer return dcr_read_mmio(host.host.mmio, dcr_n);
96b786af11SStephen Neuendorffer else /* host.type == DCR_HOST_INVALID */
97b786af11SStephen Neuendorffer WARN_ON(true);
98b786af11SStephen Neuendorffer return 0;
99b786af11SStephen Neuendorffer }
100b786af11SStephen Neuendorffer EXPORT_SYMBOL_GPL(dcr_read_generic);
101b786af11SStephen Neuendorffer
dcr_write_generic(dcr_host_t host,unsigned int dcr_n,u32 value)102b786af11SStephen Neuendorffer void dcr_write_generic(dcr_host_t host, unsigned int dcr_n, u32 value)
103b786af11SStephen Neuendorffer {
104b786af11SStephen Neuendorffer if (host.type == DCR_HOST_NATIVE)
105b786af11SStephen Neuendorffer dcr_write_native(host.host.native, dcr_n, value);
106b786af11SStephen Neuendorffer else if (host.type == DCR_HOST_MMIO)
107b786af11SStephen Neuendorffer dcr_write_mmio(host.host.mmio, dcr_n, value);
108b786af11SStephen Neuendorffer else /* host.type == DCR_HOST_INVALID */
109b786af11SStephen Neuendorffer WARN_ON(true);
110b786af11SStephen Neuendorffer }
111b786af11SStephen Neuendorffer EXPORT_SYMBOL_GPL(dcr_write_generic);
112b786af11SStephen Neuendorffer
113b786af11SStephen Neuendorffer #endif /* defined(CONFIG_PPC_DCR_NATIVE) && defined(CONFIG_PPC_DCR_MMIO) */
114b786af11SStephen Neuendorffer
dcr_resource_start(const struct device_node * np,unsigned int index)115e14d7749SGrant Erickson unsigned int dcr_resource_start(const struct device_node *np,
116e14d7749SGrant Erickson unsigned int index)
1174c75a6f4SBenjamin Herrenschmidt {
1184c75a6f4SBenjamin Herrenschmidt unsigned int ds;
119e2eb6392SStephen Rothwell const u32 *dr = of_get_property(np, "dcr-reg", &ds);
1204c75a6f4SBenjamin Herrenschmidt
1214c75a6f4SBenjamin Herrenschmidt if (dr == NULL || ds & 1 || index >= (ds / 8))
1224c75a6f4SBenjamin Herrenschmidt return 0;
1234c75a6f4SBenjamin Herrenschmidt
1244c75a6f4SBenjamin Herrenschmidt return dr[index * 2];
1254c75a6f4SBenjamin Herrenschmidt }
12696b952ddSMurali Iyer EXPORT_SYMBOL_GPL(dcr_resource_start);
1274c75a6f4SBenjamin Herrenschmidt
dcr_resource_len(const struct device_node * np,unsigned int index)128e14d7749SGrant Erickson unsigned int dcr_resource_len(const struct device_node *np, unsigned int index)
1294c75a6f4SBenjamin Herrenschmidt {
1304c75a6f4SBenjamin Herrenschmidt unsigned int ds;
131e2eb6392SStephen Rothwell const u32 *dr = of_get_property(np, "dcr-reg", &ds);
1324c75a6f4SBenjamin Herrenschmidt
1334c75a6f4SBenjamin Herrenschmidt if (dr == NULL || ds & 1 || index >= (ds / 8))
1344c75a6f4SBenjamin Herrenschmidt return 0;
1354c75a6f4SBenjamin Herrenschmidt
1364c75a6f4SBenjamin Herrenschmidt return dr[index * 2 + 1];
1374c75a6f4SBenjamin Herrenschmidt }
13896b952ddSMurali Iyer EXPORT_SYMBOL_GPL(dcr_resource_len);
1394c75a6f4SBenjamin Herrenschmidt
140b786af11SStephen Neuendorffer #ifdef CONFIG_PPC_DCR_MMIO
1414c75a6f4SBenjamin Herrenschmidt
of_translate_dcr_address(struct device_node * dev,unsigned int dcr_n,unsigned int * out_stride)142ba52464aSGrant Likely static u64 of_translate_dcr_address(struct device_node *dev,
1434c75a6f4SBenjamin Herrenschmidt unsigned int dcr_n,
1444c75a6f4SBenjamin Herrenschmidt unsigned int *out_stride)
1454c75a6f4SBenjamin Herrenschmidt {
1464c75a6f4SBenjamin Herrenschmidt struct device_node *dp;
1474c75a6f4SBenjamin Herrenschmidt const u32 *p;
1484c75a6f4SBenjamin Herrenschmidt unsigned int stride;
149b786af11SStephen Neuendorffer u64 ret = OF_BAD_ADDR;
1504c75a6f4SBenjamin Herrenschmidt
1514c75a6f4SBenjamin Herrenschmidt dp = find_dcr_parent(dev);
1524c75a6f4SBenjamin Herrenschmidt if (dp == NULL)
1534c75a6f4SBenjamin Herrenschmidt return OF_BAD_ADDR;
1544c75a6f4SBenjamin Herrenschmidt
1554c75a6f4SBenjamin Herrenschmidt /* Stride is not properly defined yet, default to 0x10 for Axon */
156e2eb6392SStephen Rothwell p = of_get_property(dp, "dcr-mmio-stride", NULL);
1574c75a6f4SBenjamin Herrenschmidt stride = (p == NULL) ? 0x10 : *p;
1584c75a6f4SBenjamin Herrenschmidt
1594c75a6f4SBenjamin Herrenschmidt /* XXX FIXME: Which property name is to use of the 2 following ? */
160e2eb6392SStephen Rothwell p = of_get_property(dp, "dcr-mmio-range", NULL);
1614c75a6f4SBenjamin Herrenschmidt if (p == NULL)
162e2eb6392SStephen Rothwell p = of_get_property(dp, "dcr-mmio-space", NULL);
1634c75a6f4SBenjamin Herrenschmidt if (p == NULL)
164b786af11SStephen Neuendorffer goto done;
1654c75a6f4SBenjamin Herrenschmidt
1664c75a6f4SBenjamin Herrenschmidt /* Maybe could do some better range checking here */
1674c75a6f4SBenjamin Herrenschmidt ret = of_translate_address(dp, p);
1684c75a6f4SBenjamin Herrenschmidt if (ret != OF_BAD_ADDR)
1694c75a6f4SBenjamin Herrenschmidt ret += (u64)(stride) * (u64)dcr_n;
1704c75a6f4SBenjamin Herrenschmidt if (out_stride)
1714c75a6f4SBenjamin Herrenschmidt *out_stride = stride;
172b786af11SStephen Neuendorffer
173b786af11SStephen Neuendorffer done:
174b786af11SStephen Neuendorffer of_node_put(dp);
1754c75a6f4SBenjamin Herrenschmidt return ret;
1764c75a6f4SBenjamin Herrenschmidt }
1774c75a6f4SBenjamin Herrenschmidt
dcr_map_mmio(struct device_node * dev,unsigned int dcr_n,unsigned int dcr_c)178b786af11SStephen Neuendorffer dcr_host_mmio_t dcr_map_mmio(struct device_node *dev,
179b786af11SStephen Neuendorffer unsigned int dcr_n,
1804c75a6f4SBenjamin Herrenschmidt unsigned int dcr_c)
1814c75a6f4SBenjamin Herrenschmidt {
182b786af11SStephen Neuendorffer dcr_host_mmio_t ret = { .token = NULL, .stride = 0, .base = dcr_n };
1834c75a6f4SBenjamin Herrenschmidt u64 addr;
1844c75a6f4SBenjamin Herrenschmidt
185b7c670d6SRob Herring pr_debug("dcr_map(%pOF, 0x%x, 0x%x)\n",
186b7c670d6SRob Herring dev, dcr_n, dcr_c);
1874c75a6f4SBenjamin Herrenschmidt
1884c75a6f4SBenjamin Herrenschmidt addr = of_translate_dcr_address(dev, dcr_n, &ret.stride);
189b786af11SStephen Neuendorffer pr_debug("translates to addr: 0x%llx, stride: 0x%x\n",
190b786af11SStephen Neuendorffer (unsigned long long) addr, ret.stride);
1914c75a6f4SBenjamin Herrenschmidt if (addr == OF_BAD_ADDR)
1924c75a6f4SBenjamin Herrenschmidt return ret;
1934c75a6f4SBenjamin Herrenschmidt pr_debug("mapping 0x%x bytes\n", dcr_c * ret.stride);
1944c75a6f4SBenjamin Herrenschmidt ret.token = ioremap(addr, dcr_c * ret.stride);
1954c75a6f4SBenjamin Herrenschmidt if (ret.token == NULL)
1964c75a6f4SBenjamin Herrenschmidt return ret;
1974c75a6f4SBenjamin Herrenschmidt pr_debug("mapped at 0x%p -> base is 0x%p\n",
1984c75a6f4SBenjamin Herrenschmidt ret.token, ret.token - dcr_n * ret.stride);
1994c75a6f4SBenjamin Herrenschmidt ret.token -= dcr_n * ret.stride;
2004c75a6f4SBenjamin Herrenschmidt return ret;
2014c75a6f4SBenjamin Herrenschmidt }
202b786af11SStephen Neuendorffer EXPORT_SYMBOL_GPL(dcr_map_mmio);
2034c75a6f4SBenjamin Herrenschmidt
dcr_unmap_mmio(dcr_host_mmio_t host,unsigned int dcr_c)204b786af11SStephen Neuendorffer void dcr_unmap_mmio(dcr_host_mmio_t host, unsigned int dcr_c)
2054c75a6f4SBenjamin Herrenschmidt {
206b786af11SStephen Neuendorffer dcr_host_mmio_t h = host;
2074c75a6f4SBenjamin Herrenschmidt
2084c75a6f4SBenjamin Herrenschmidt if (h.token == NULL)
2094c75a6f4SBenjamin Herrenschmidt return;
210cdbd3865SMichael Ellerman h.token += host.base * h.stride;
2114c75a6f4SBenjamin Herrenschmidt iounmap(h.token);
2124c75a6f4SBenjamin Herrenschmidt h.token = NULL;
2134c75a6f4SBenjamin Herrenschmidt }
214b786af11SStephen Neuendorffer EXPORT_SYMBOL_GPL(dcr_unmap_mmio);
215b786af11SStephen Neuendorffer
216b786af11SStephen Neuendorffer #endif /* defined(CONFIG_PPC_DCR_MMIO) */
217b786af11SStephen Neuendorffer
218b786af11SStephen Neuendorffer #ifdef CONFIG_PPC_DCR_NATIVE
219853265e5SValentine Barshak DEFINE_SPINLOCK(dcr_ind_lock);
22022e55fcfSPranith Kumar EXPORT_SYMBOL_GPL(dcr_ind_lock);
221b786af11SStephen Neuendorffer #endif /* defined(CONFIG_PPC_DCR_NATIVE) */
222b786af11SStephen Neuendorffer
223