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 <asm/prom.h> 12 #include <asm/dcr.h> 13 14 #ifdef CONFIG_PPC_DCR_MMIO 15 static 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 37 bool 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 } 46 EXPORT_SYMBOL_GPL(dcr_map_ok_generic); 47 48 dcr_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 } 77 EXPORT_SYMBOL_GPL(dcr_map_generic); 78 79 void 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 } 88 EXPORT_SYMBOL_GPL(dcr_unmap_generic); 89 90 u32 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 } 100 EXPORT_SYMBOL_GPL(dcr_read_generic); 101 102 void 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 } 111 EXPORT_SYMBOL_GPL(dcr_write_generic); 112 113 #endif /* defined(CONFIG_PPC_DCR_NATIVE) && defined(CONFIG_PPC_DCR_MMIO) */ 114 115 unsigned 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 } 126 EXPORT_SYMBOL_GPL(dcr_resource_start); 127 128 unsigned 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 } 138 EXPORT_SYMBOL_GPL(dcr_resource_len); 139 140 #ifdef CONFIG_PPC_DCR_MMIO 141 142 static 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 178 dcr_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 } 202 EXPORT_SYMBOL_GPL(dcr_map_mmio); 203 204 void 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 } 214 EXPORT_SYMBOL_GPL(dcr_unmap_mmio); 215 216 #endif /* defined(CONFIG_PPC_DCR_MMIO) */ 217 218 #ifdef CONFIG_PPC_DCR_NATIVE 219 DEFINE_SPINLOCK(dcr_ind_lock); 220 EXPORT_SYMBOL_GPL(dcr_ind_lock); 221 #endif /* defined(CONFIG_PPC_DCR_NATIVE) */ 222 223