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 <core/subdev.h> 25 #include <core/device.h> 26 #include <core/option.h> 27 #include <subdev/mc.h> 28 29 const char * 30 nvkm_subdev_type[NVKM_SUBDEV_NR] = { 31 #define NVKM_LAYOUT_ONCE(type,data,ptr,...) [type] = #ptr, 32 #define NVKM_LAYOUT_INST(A...) NVKM_LAYOUT_ONCE(A) 33 #include <core/layout.h> 34 #undef NVKM_LAYOUT_ONCE 35 #undef NVKM_LAYOUT_INST 36 }; 37 38 void nvkm_subdev_intr(struct nvkm_subdev * subdev)39 nvkm_subdev_intr(struct nvkm_subdev *subdev) 40 { 41 if (subdev->func->intr) 42 subdev->func->intr(subdev); 43 } 44 45 int nvkm_subdev_info(struct nvkm_subdev * subdev,u64 mthd,u64 * data)46 nvkm_subdev_info(struct nvkm_subdev *subdev, u64 mthd, u64 *data) 47 { 48 if (subdev->func->info) 49 return subdev->func->info(subdev, mthd, data); 50 return -ENOSYS; 51 } 52 53 int nvkm_subdev_fini(struct nvkm_subdev * subdev,bool suspend)54 nvkm_subdev_fini(struct nvkm_subdev *subdev, bool suspend) 55 { 56 struct nvkm_device *device = subdev->device; 57 const char *action = suspend ? "suspend" : subdev->use.enabled ? "fini" : "reset"; 58 s64 time; 59 60 nvkm_trace(subdev, "%s running...\n", action); 61 time = ktime_to_us(ktime_get()); 62 63 if (subdev->func->fini) { 64 int ret = subdev->func->fini(subdev, suspend); 65 if (ret) { 66 nvkm_error(subdev, "%s failed, %d\n", action, ret); 67 if (suspend) 68 return ret; 69 } 70 } 71 subdev->use.enabled = false; 72 73 nvkm_mc_reset(device, subdev->type, subdev->inst); 74 75 time = ktime_to_us(ktime_get()) - time; 76 nvkm_trace(subdev, "%s completed in %lldus\n", action, time); 77 return 0; 78 } 79 80 int nvkm_subdev_preinit(struct nvkm_subdev * subdev)81 nvkm_subdev_preinit(struct nvkm_subdev *subdev) 82 { 83 s64 time; 84 85 nvkm_trace(subdev, "preinit running...\n"); 86 time = ktime_to_us(ktime_get()); 87 88 if (subdev->func->preinit) { 89 int ret = subdev->func->preinit(subdev); 90 if (ret) { 91 nvkm_error(subdev, "preinit failed, %d\n", ret); 92 return ret; 93 } 94 } 95 96 time = ktime_to_us(ktime_get()) - time; 97 nvkm_trace(subdev, "preinit completed in %lldus\n", time); 98 return 0; 99 } 100 101 static int nvkm_subdev_oneinit_(struct nvkm_subdev * subdev)102 nvkm_subdev_oneinit_(struct nvkm_subdev *subdev) 103 { 104 s64 time; 105 int ret; 106 107 if (!subdev->func->oneinit || subdev->oneinit) 108 return 0; 109 110 nvkm_trace(subdev, "one-time init running...\n"); 111 time = ktime_to_us(ktime_get()); 112 ret = subdev->func->oneinit(subdev); 113 if (ret) { 114 nvkm_error(subdev, "one-time init failed, %d\n", ret); 115 return ret; 116 } 117 118 subdev->oneinit = true; 119 time = ktime_to_us(ktime_get()) - time; 120 nvkm_trace(subdev, "one-time init completed in %lldus\n", time); 121 return 0; 122 } 123 124 static int nvkm_subdev_init_(struct nvkm_subdev * subdev)125 nvkm_subdev_init_(struct nvkm_subdev *subdev) 126 { 127 s64 time; 128 int ret; 129 130 if (subdev->use.enabled) { 131 nvkm_trace(subdev, "init skipped, already running\n"); 132 return 0; 133 } 134 135 nvkm_trace(subdev, "init running...\n"); 136 time = ktime_to_us(ktime_get()); 137 138 ret = nvkm_subdev_oneinit_(subdev); 139 if (ret) 140 return ret; 141 142 subdev->use.enabled = true; 143 144 if (subdev->func->init) { 145 ret = subdev->func->init(subdev); 146 if (ret) { 147 nvkm_error(subdev, "init failed, %d\n", ret); 148 return ret; 149 } 150 } 151 152 time = ktime_to_us(ktime_get()) - time; 153 nvkm_trace(subdev, "init completed in %lldus\n", time); 154 return 0; 155 } 156 157 int nvkm_subdev_init(struct nvkm_subdev * subdev)158 nvkm_subdev_init(struct nvkm_subdev *subdev) 159 { 160 int ret; 161 162 mutex_lock(&subdev->use.mutex); 163 if (refcount_read(&subdev->use.refcount) == 0) { 164 nvkm_trace(subdev, "init skipped, no users\n"); 165 mutex_unlock(&subdev->use.mutex); 166 return 0; 167 } 168 169 ret = nvkm_subdev_init_(subdev); 170 mutex_unlock(&subdev->use.mutex); 171 return ret; 172 } 173 174 int nvkm_subdev_oneinit(struct nvkm_subdev * subdev)175 nvkm_subdev_oneinit(struct nvkm_subdev *subdev) 176 { 177 int ret; 178 179 mutex_lock(&subdev->use.mutex); 180 ret = nvkm_subdev_oneinit_(subdev); 181 mutex_unlock(&subdev->use.mutex); 182 return ret; 183 } 184 185 void nvkm_subdev_unref(struct nvkm_subdev * subdev)186 nvkm_subdev_unref(struct nvkm_subdev *subdev) 187 { 188 if (refcount_dec_and_mutex_lock(&subdev->use.refcount, &subdev->use.mutex)) { 189 nvkm_subdev_fini(subdev, false); 190 mutex_unlock(&subdev->use.mutex); 191 } 192 } 193 194 int nvkm_subdev_ref(struct nvkm_subdev * subdev)195 nvkm_subdev_ref(struct nvkm_subdev *subdev) 196 { 197 int ret; 198 199 if (subdev && !refcount_inc_not_zero(&subdev->use.refcount)) { 200 mutex_lock(&subdev->use.mutex); 201 if (!refcount_inc_not_zero(&subdev->use.refcount)) { 202 if ((ret = nvkm_subdev_init_(subdev))) { 203 mutex_unlock(&subdev->use.mutex); 204 return ret; 205 } 206 207 refcount_set(&subdev->use.refcount, 1); 208 } 209 mutex_unlock(&subdev->use.mutex); 210 } 211 212 return 0; 213 } 214 215 void nvkm_subdev_del(struct nvkm_subdev ** psubdev)216 nvkm_subdev_del(struct nvkm_subdev **psubdev) 217 { 218 struct nvkm_subdev *subdev = *psubdev; 219 s64 time; 220 221 if (subdev && !WARN_ON(!subdev->func)) { 222 nvkm_trace(subdev, "destroy running...\n"); 223 time = ktime_to_us(ktime_get()); 224 list_del(&subdev->head); 225 if (subdev->func->dtor) 226 *psubdev = subdev->func->dtor(subdev); 227 mutex_destroy(&subdev->use.mutex); 228 time = ktime_to_us(ktime_get()) - time; 229 nvkm_trace(subdev, "destroy completed in %lldus\n", time); 230 kfree(*psubdev); 231 *psubdev = NULL; 232 } 233 } 234 235 void nvkm_subdev_disable(struct nvkm_device * device,enum nvkm_subdev_type type,int inst)236 nvkm_subdev_disable(struct nvkm_device *device, enum nvkm_subdev_type type, int inst) 237 { 238 struct nvkm_subdev *subdev; 239 list_for_each_entry(subdev, &device->subdev, head) { 240 if (subdev->type == type && subdev->inst == inst) { 241 *subdev->pself = NULL; 242 nvkm_subdev_del(&subdev); 243 break; 244 } 245 } 246 } 247 248 void __nvkm_subdev_ctor(const struct nvkm_subdev_func * func,struct nvkm_device * device,enum nvkm_subdev_type type,int inst,struct nvkm_subdev * subdev)249 __nvkm_subdev_ctor(const struct nvkm_subdev_func *func, struct nvkm_device *device, 250 enum nvkm_subdev_type type, int inst, struct nvkm_subdev *subdev) 251 { 252 subdev->func = func; 253 subdev->device = device; 254 subdev->type = type; 255 subdev->inst = inst < 0 ? 0 : inst; 256 257 if (inst >= 0) 258 snprintf(subdev->name, sizeof(subdev->name), "%s%d", nvkm_subdev_type[type], inst); 259 else 260 strscpy(subdev->name, nvkm_subdev_type[type], sizeof(subdev->name)); 261 subdev->debug = nvkm_dbgopt(device->dbgopt, subdev->name); 262 263 refcount_set(&subdev->use.refcount, 1); 264 list_add_tail(&subdev->head, &device->subdev); 265 } 266 267 int nvkm_subdev_new_(const struct nvkm_subdev_func * func,struct nvkm_device * device,enum nvkm_subdev_type type,int inst,struct nvkm_subdev ** psubdev)268 nvkm_subdev_new_(const struct nvkm_subdev_func *func, struct nvkm_device *device, 269 enum nvkm_subdev_type type, int inst, struct nvkm_subdev **psubdev) 270 { 271 if (!(*psubdev = kzalloc(sizeof(**psubdev), GFP_KERNEL))) 272 return -ENOMEM; 273 nvkm_subdev_ctor(func, device, type, inst, *psubdev); 274 return 0; 275 } 276