276 lines · c
1/*2 * Copyright 2012 Red Hat Inc.3 *4 * Permission is hereby granted, free of charge, to any person obtaining a5 * copy of this software and associated documentation files (the "Software"),6 * to deal in the Software without restriction, including without limitation7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,8 * and/or sell copies of the Software, and to permit persons to whom the9 * Software is furnished to do so, subject to the following conditions:10 *11 * The above copyright notice and this permission notice shall be included in12 * all copies or substantial portions of the Software.13 *14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR18 * 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 OR20 * OTHER DEALINGS IN THE SOFTWARE.21 *22 * Authors: Ben Skeggs23 */24#include <core/subdev.h>25#include <core/device.h>26#include <core/option.h>27#include <subdev/mc.h>28 29const char *30nvkm_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_ONCE35#undef NVKM_LAYOUT_INST36};37 38void39nvkm_subdev_intr(struct nvkm_subdev *subdev)40{41 if (subdev->func->intr)42 subdev->func->intr(subdev);43}44 45int46nvkm_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 53int54nvkm_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 80int81nvkm_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 101static int102nvkm_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 124static int125nvkm_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 157int158nvkm_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 174int175nvkm_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 185void186nvkm_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 194int195nvkm_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 215void216nvkm_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 235void236nvkm_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 248void249__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 else260 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 267int268nvkm_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