dcr.c (5507B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * (c) Copyright 2006 Benjamin Herrenschmidt, IBM Corp. 4 * <benh@kernel.crashing.org> 5 */ 6 7#undef DEBUG 8 9#include <linux/kernel.h> 10#include <linux/export.h> 11#include <linux/of_address.h> 12#include <asm/dcr.h> 13 14#ifdef CONFIG_PPC_DCR_MMIO 15static struct device_node *find_dcr_parent(struct device_node *node) 16{ 17 struct device_node *par, *tmp; 18 const u32 *p; 19 20 for (par = of_node_get(node); par;) { 21 if (of_get_property(par, "dcr-controller", NULL)) 22 break; 23 p = of_get_property(par, "dcr-parent", NULL); 24 tmp = par; 25 if (p == NULL) 26 par = of_get_parent(par); 27 else 28 par = of_find_node_by_phandle(*p); 29 of_node_put(tmp); 30 } 31 return par; 32} 33#endif 34 35#if defined(CONFIG_PPC_DCR_NATIVE) && defined(CONFIG_PPC_DCR_MMIO) 36 37bool dcr_map_ok_generic(dcr_host_t host) 38{ 39 if (host.type == DCR_HOST_NATIVE) 40 return dcr_map_ok_native(host.host.native); 41 else if (host.type == DCR_HOST_MMIO) 42 return dcr_map_ok_mmio(host.host.mmio); 43 else 44 return false; 45} 46EXPORT_SYMBOL_GPL(dcr_map_ok_generic); 47 48dcr_host_t dcr_map_generic(struct device_node *dev, 49 unsigned int dcr_n, 50 unsigned int dcr_c) 51{ 52 dcr_host_t host; 53 struct device_node *dp; 54 const char *prop; 55 56 host.type = DCR_HOST_INVALID; 57 58 dp = find_dcr_parent(dev); 59 if (dp == NULL) 60 return host; 61 62 prop = of_get_property(dp, "dcr-access-method", NULL); 63 64 pr_debug("dcr_map_generic(dcr-access-method = %s)\n", prop); 65 66 if (!strcmp(prop, "native")) { 67 host.type = DCR_HOST_NATIVE; 68 host.host.native = dcr_map_native(dev, dcr_n, dcr_c); 69 } else if (!strcmp(prop, "mmio")) { 70 host.type = DCR_HOST_MMIO; 71 host.host.mmio = dcr_map_mmio(dev, dcr_n, dcr_c); 72 } 73 74 of_node_put(dp); 75 return host; 76} 77EXPORT_SYMBOL_GPL(dcr_map_generic); 78 79void dcr_unmap_generic(dcr_host_t host, unsigned int dcr_c) 80{ 81 if (host.type == DCR_HOST_NATIVE) 82 dcr_unmap_native(host.host.native, dcr_c); 83 else if (host.type == DCR_HOST_MMIO) 84 dcr_unmap_mmio(host.host.mmio, dcr_c); 85 else /* host.type == DCR_HOST_INVALID */ 86 WARN_ON(true); 87} 88EXPORT_SYMBOL_GPL(dcr_unmap_generic); 89 90u32 dcr_read_generic(dcr_host_t host, unsigned int dcr_n) 91{ 92 if (host.type == DCR_HOST_NATIVE) 93 return dcr_read_native(host.host.native, dcr_n); 94 else if (host.type == DCR_HOST_MMIO) 95 return dcr_read_mmio(host.host.mmio, dcr_n); 96 else /* host.type == DCR_HOST_INVALID */ 97 WARN_ON(true); 98 return 0; 99} 100EXPORT_SYMBOL_GPL(dcr_read_generic); 101 102void dcr_write_generic(dcr_host_t host, unsigned int dcr_n, u32 value) 103{ 104 if (host.type == DCR_HOST_NATIVE) 105 dcr_write_native(host.host.native, dcr_n, value); 106 else if (host.type == DCR_HOST_MMIO) 107 dcr_write_mmio(host.host.mmio, dcr_n, value); 108 else /* host.type == DCR_HOST_INVALID */ 109 WARN_ON(true); 110} 111EXPORT_SYMBOL_GPL(dcr_write_generic); 112 113#endif /* defined(CONFIG_PPC_DCR_NATIVE) && defined(CONFIG_PPC_DCR_MMIO) */ 114 115unsigned int dcr_resource_start(const struct device_node *np, 116 unsigned int index) 117{ 118 unsigned int ds; 119 const u32 *dr = of_get_property(np, "dcr-reg", &ds); 120 121 if (dr == NULL || ds & 1 || index >= (ds / 8)) 122 return 0; 123 124 return dr[index * 2]; 125} 126EXPORT_SYMBOL_GPL(dcr_resource_start); 127 128unsigned int dcr_resource_len(const struct device_node *np, unsigned int index) 129{ 130 unsigned int ds; 131 const u32 *dr = of_get_property(np, "dcr-reg", &ds); 132 133 if (dr == NULL || ds & 1 || index >= (ds / 8)) 134 return 0; 135 136 return dr[index * 2 + 1]; 137} 138EXPORT_SYMBOL_GPL(dcr_resource_len); 139 140#ifdef CONFIG_PPC_DCR_MMIO 141 142static u64 of_translate_dcr_address(struct device_node *dev, 143 unsigned int dcr_n, 144 unsigned int *out_stride) 145{ 146 struct device_node *dp; 147 const u32 *p; 148 unsigned int stride; 149 u64 ret = OF_BAD_ADDR; 150 151 dp = find_dcr_parent(dev); 152 if (dp == NULL) 153 return OF_BAD_ADDR; 154 155 /* Stride is not properly defined yet, default to 0x10 for Axon */ 156 p = of_get_property(dp, "dcr-mmio-stride", NULL); 157 stride = (p == NULL) ? 0x10 : *p; 158 159 /* XXX FIXME: Which property name is to use of the 2 following ? */ 160 p = of_get_property(dp, "dcr-mmio-range", NULL); 161 if (p == NULL) 162 p = of_get_property(dp, "dcr-mmio-space", NULL); 163 if (p == NULL) 164 goto done; 165 166 /* Maybe could do some better range checking here */ 167 ret = of_translate_address(dp, p); 168 if (ret != OF_BAD_ADDR) 169 ret += (u64)(stride) * (u64)dcr_n; 170 if (out_stride) 171 *out_stride = stride; 172 173 done: 174 of_node_put(dp); 175 return ret; 176} 177 178dcr_host_mmio_t dcr_map_mmio(struct device_node *dev, 179 unsigned int dcr_n, 180 unsigned int dcr_c) 181{ 182 dcr_host_mmio_t ret = { .token = NULL, .stride = 0, .base = dcr_n }; 183 u64 addr; 184 185 pr_debug("dcr_map(%pOF, 0x%x, 0x%x)\n", 186 dev, dcr_n, dcr_c); 187 188 addr = of_translate_dcr_address(dev, dcr_n, &ret.stride); 189 pr_debug("translates to addr: 0x%llx, stride: 0x%x\n", 190 (unsigned long long) addr, ret.stride); 191 if (addr == OF_BAD_ADDR) 192 return ret; 193 pr_debug("mapping 0x%x bytes\n", dcr_c * ret.stride); 194 ret.token = ioremap(addr, dcr_c * ret.stride); 195 if (ret.token == NULL) 196 return ret; 197 pr_debug("mapped at 0x%p -> base is 0x%p\n", 198 ret.token, ret.token - dcr_n * ret.stride); 199 ret.token -= dcr_n * ret.stride; 200 return ret; 201} 202EXPORT_SYMBOL_GPL(dcr_map_mmio); 203 204void dcr_unmap_mmio(dcr_host_mmio_t host, unsigned int dcr_c) 205{ 206 dcr_host_mmio_t h = host; 207 208 if (h.token == NULL) 209 return; 210 h.token += host.base * h.stride; 211 iounmap(h.token); 212 h.token = NULL; 213} 214EXPORT_SYMBOL_GPL(dcr_unmap_mmio); 215 216#endif /* defined(CONFIG_PPC_DCR_MMIO) */ 217 218#ifdef CONFIG_PPC_DCR_NATIVE 219DEFINE_SPINLOCK(dcr_ind_lock); 220EXPORT_SYMBOL_GPL(dcr_ind_lock); 221#endif /* defined(CONFIG_PPC_DCR_NATIVE) */ 222