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