1 /* 2 * Copyright 2012 Red Hat Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Authors: Ben Skeggs 23 */ 24 #include "priv.h" 25 26 #include <core/memory.h> 27 #include <subdev/bar.h> 28 29 /****************************************************************************** 30 * instmem object base implementation 31 *****************************************************************************/ 32 #define nvkm_instobj(p) container_of((p), struct nvkm_instobj, memory) 33 34 struct nvkm_instobj { 35 struct nvkm_memory memory; 36 struct nvkm_memory *parent; 37 struct nvkm_instmem *imem; 38 struct list_head head; 39 u32 *suspend; 40 void __iomem *map; 41 }; 42 43 static enum nvkm_memory_target 44 nvkm_instobj_target(struct nvkm_memory *memory) 45 { 46 memory = nvkm_instobj(memory)->parent; 47 return nvkm_memory_target(memory); 48 } 49 50 static u64 51 nvkm_instobj_addr(struct nvkm_memory *memory) 52 { 53 memory = nvkm_instobj(memory)->parent; 54 return nvkm_memory_addr(memory); 55 } 56 57 static u64 58 nvkm_instobj_size(struct nvkm_memory *memory) 59 { 60 memory = nvkm_instobj(memory)->parent; 61 return nvkm_memory_size(memory); 62 } 63 64 static void 65 nvkm_instobj_release(struct nvkm_memory *memory) 66 { 67 struct nvkm_instobj *iobj = nvkm_instobj(memory); 68 nvkm_bar_flush(iobj->imem->subdev.device->bar); 69 } 70 71 static void __iomem * 72 nvkm_instobj_acquire(struct nvkm_memory *memory) 73 { 74 return nvkm_instobj(memory)->map; 75 } 76 77 static u32 78 nvkm_instobj_rd32(struct nvkm_memory *memory, u64 offset) 79 { 80 return ioread32_native(nvkm_instobj(memory)->map + offset); 81 } 82 83 static void 84 nvkm_instobj_wr32(struct nvkm_memory *memory, u64 offset, u32 data) 85 { 86 iowrite32_native(data, nvkm_instobj(memory)->map + offset); 87 } 88 89 static void 90 nvkm_instobj_map(struct nvkm_memory *memory, struct nvkm_vma *vma, u64 offset) 91 { 92 memory = nvkm_instobj(memory)->parent; 93 nvkm_memory_map(memory, vma, offset); 94 } 95 96 static void * 97 nvkm_instobj_dtor(struct nvkm_memory *memory) 98 { 99 struct nvkm_instobj *iobj = nvkm_instobj(memory); 100 list_del(&iobj->head); 101 nvkm_memory_del(&iobj->parent); 102 return iobj; 103 } 104 105 const struct nvkm_memory_func 106 nvkm_instobj_func = { 107 .dtor = nvkm_instobj_dtor, 108 .target = nvkm_instobj_target, 109 .addr = nvkm_instobj_addr, 110 .size = nvkm_instobj_size, 111 .acquire = nvkm_instobj_acquire, 112 .release = nvkm_instobj_release, 113 .rd32 = nvkm_instobj_rd32, 114 .wr32 = nvkm_instobj_wr32, 115 .map = nvkm_instobj_map, 116 }; 117 118 static void 119 nvkm_instobj_boot(struct nvkm_memory *memory, struct nvkm_vm *vm) 120 { 121 memory = nvkm_instobj(memory)->parent; 122 nvkm_memory_boot(memory, vm); 123 } 124 125 static void 126 nvkm_instobj_release_slow(struct nvkm_memory *memory) 127 { 128 struct nvkm_instobj *iobj = nvkm_instobj(memory); 129 nvkm_instobj_release(memory); 130 nvkm_done(iobj->parent); 131 } 132 133 static void __iomem * 134 nvkm_instobj_acquire_slow(struct nvkm_memory *memory) 135 { 136 struct nvkm_instobj *iobj = nvkm_instobj(memory); 137 iobj->map = nvkm_kmap(iobj->parent); 138 if (iobj->map) 139 memory->func = &nvkm_instobj_func; 140 return iobj->map; 141 } 142 143 static u32 144 nvkm_instobj_rd32_slow(struct nvkm_memory *memory, u64 offset) 145 { 146 struct nvkm_instobj *iobj = nvkm_instobj(memory); 147 return nvkm_ro32(iobj->parent, offset); 148 } 149 150 static void 151 nvkm_instobj_wr32_slow(struct nvkm_memory *memory, u64 offset, u32 data) 152 { 153 struct nvkm_instobj *iobj = nvkm_instobj(memory); 154 return nvkm_wo32(iobj->parent, offset, data); 155 } 156 157 const struct nvkm_memory_func 158 nvkm_instobj_func_slow = { 159 .dtor = nvkm_instobj_dtor, 160 .target = nvkm_instobj_target, 161 .addr = nvkm_instobj_addr, 162 .size = nvkm_instobj_size, 163 .boot = nvkm_instobj_boot, 164 .acquire = nvkm_instobj_acquire_slow, 165 .release = nvkm_instobj_release_slow, 166 .rd32 = nvkm_instobj_rd32_slow, 167 .wr32 = nvkm_instobj_wr32_slow, 168 .map = nvkm_instobj_map, 169 }; 170 171 int 172 nvkm_instobj_new(struct nvkm_instmem *imem, u32 size, u32 align, bool zero, 173 struct nvkm_memory **pmemory) 174 { 175 struct nvkm_memory *memory = NULL; 176 struct nvkm_instobj *iobj; 177 u32 offset; 178 int ret; 179 180 ret = imem->func->memory_new(imem, size, align, zero, &memory); 181 if (ret) 182 goto done; 183 184 if (!imem->func->persistent) { 185 if (!(iobj = kzalloc(sizeof(*iobj), GFP_KERNEL))) { 186 ret = -ENOMEM; 187 goto done; 188 } 189 190 nvkm_memory_ctor(&nvkm_instobj_func_slow, &iobj->memory); 191 iobj->parent = memory; 192 iobj->imem = imem; 193 list_add_tail(&iobj->head, &imem->list); 194 memory = &iobj->memory; 195 } 196 197 if (!imem->func->zero && zero) { 198 void __iomem *map = nvkm_kmap(memory); 199 if (unlikely(!map)) { 200 for (offset = 0; offset < size; offset += 4) 201 nvkm_wo32(memory, offset, 0x00000000); 202 } else { 203 memset_io(map, 0x00, size); 204 } 205 nvkm_done(memory); 206 } 207 208 done: 209 if (ret) 210 nvkm_memory_del(&memory); 211 *pmemory = memory; 212 return ret; 213 } 214 215 /****************************************************************************** 216 * instmem subdev base implementation 217 *****************************************************************************/ 218 219 u32 220 nvkm_instmem_rd32(struct nvkm_instmem *imem, u32 addr) 221 { 222 return imem->func->rd32(imem, addr); 223 } 224 225 void 226 nvkm_instmem_wr32(struct nvkm_instmem *imem, u32 addr, u32 data) 227 { 228 return imem->func->wr32(imem, addr, data); 229 } 230 231 static int 232 nvkm_instmem_fini(struct nvkm_subdev *subdev, bool suspend) 233 { 234 struct nvkm_instmem *imem = nvkm_instmem(subdev); 235 struct nvkm_instobj *iobj; 236 int i; 237 238 if (imem->func->fini) 239 imem->func->fini(imem); 240 241 if (suspend) { 242 list_for_each_entry(iobj, &imem->list, head) { 243 struct nvkm_memory *memory = iobj->parent; 244 u64 size = nvkm_memory_size(memory); 245 246 iobj->suspend = vmalloc(size); 247 if (!iobj->suspend) 248 return -ENOMEM; 249 250 for (i = 0; i < size; i += 4) 251 iobj->suspend[i / 4] = nvkm_ro32(memory, i); 252 } 253 } 254 255 return 0; 256 } 257 258 static int 259 nvkm_instmem_oneinit(struct nvkm_subdev *subdev) 260 { 261 struct nvkm_instmem *imem = nvkm_instmem(subdev); 262 if (imem->func->oneinit) 263 return imem->func->oneinit(imem); 264 return 0; 265 } 266 267 static int 268 nvkm_instmem_init(struct nvkm_subdev *subdev) 269 { 270 struct nvkm_instmem *imem = nvkm_instmem(subdev); 271 struct nvkm_instobj *iobj; 272 int i; 273 274 list_for_each_entry(iobj, &imem->list, head) { 275 if (iobj->suspend) { 276 struct nvkm_memory *memory = iobj->parent; 277 u64 size = nvkm_memory_size(memory); 278 for (i = 0; i < size; i += 4) 279 nvkm_wo32(memory, i, iobj->suspend[i / 4]); 280 vfree(iobj->suspend); 281 iobj->suspend = NULL; 282 } 283 } 284 285 return 0; 286 } 287 288 static void * 289 nvkm_instmem_dtor(struct nvkm_subdev *subdev) 290 { 291 struct nvkm_instmem *imem = nvkm_instmem(subdev); 292 if (imem->func->dtor) 293 return imem->func->dtor(imem); 294 return imem; 295 } 296 297 static const struct nvkm_subdev_func 298 nvkm_instmem = { 299 .dtor = nvkm_instmem_dtor, 300 .oneinit = nvkm_instmem_oneinit, 301 .init = nvkm_instmem_init, 302 .fini = nvkm_instmem_fini, 303 }; 304 305 void 306 nvkm_instmem_ctor(const struct nvkm_instmem_func *func, 307 struct nvkm_device *device, int index, 308 struct nvkm_instmem *imem) 309 { 310 nvkm_subdev_ctor(&nvkm_instmem, device, index, 0, &imem->subdev); 311 imem->func = func; 312 INIT_LIST_HEAD(&imem->list); 313 } 314