1 /* $NetBSD: nouveau_nvkm_engine_device_ctrl.c,v 1.3 2021/12/18 23:45:34 riastradh Exp $ */ 2 3 /* 4 * Copyright 2013 Red Hat Inc. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 * 24 * Authors: Ben Skeggs <bskeggs (at) redhat.com> 25 */ 26 #include <sys/cdefs.h> 27 __KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_engine_device_ctrl.c,v 1.3 2021/12/18 23:45:34 riastradh Exp $"); 28 29 #include "ctrl.h" 30 31 #include <core/client.h> 32 #include <subdev/clk.h> 33 34 #include <nvif/class.h> 35 #include <nvif/if0001.h> 36 #include <nvif/ioctl.h> 37 #include <nvif/unpack.h> 38 39 static int 40 nvkm_control_mthd_pstate_info(struct nvkm_control *ctrl, void *data, u32 size) 41 { 42 union { 43 struct nvif_control_pstate_info_v0 v0; 44 } *args = data; 45 struct nvkm_clk *clk = ctrl->device->clk; 46 int ret = -ENOSYS; 47 48 nvif_ioctl(&ctrl->object, "control pstate info size %d\n", size); 49 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { 50 nvif_ioctl(&ctrl->object, "control pstate info vers %d\n", 51 args->v0.version); 52 } else 53 return ret; 54 55 if (clk) { 56 args->v0.count = clk->state_nr; 57 args->v0.ustate_ac = clk->ustate_ac; 58 args->v0.ustate_dc = clk->ustate_dc; 59 args->v0.pwrsrc = clk->pwrsrc; 60 args->v0.pstate = clk->pstate; 61 } else { 62 args->v0.count = 0; 63 args->v0.ustate_ac = NVIF_CONTROL_PSTATE_INFO_V0_USTATE_DISABLE; 64 args->v0.ustate_dc = NVIF_CONTROL_PSTATE_INFO_V0_USTATE_DISABLE; 65 args->v0.pwrsrc = -ENOSYS; 66 args->v0.pstate = NVIF_CONTROL_PSTATE_INFO_V0_PSTATE_UNKNOWN; 67 } 68 69 return 0; 70 } 71 72 static int 73 nvkm_control_mthd_pstate_attr(struct nvkm_control *ctrl, void *data, u32 size) 74 { 75 union { 76 struct nvif_control_pstate_attr_v0 v0; 77 } *args = data; 78 struct nvkm_clk *clk = ctrl->device->clk; 79 const struct nvkm_domain *domain; 80 struct nvkm_pstate *pstate; 81 struct nvkm_cstate *cstate; 82 int i = 0, j = -1; 83 u32 lo, hi; 84 int ret = -ENOSYS; 85 86 nvif_ioctl(&ctrl->object, "control pstate attr size %d\n", size); 87 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { 88 nvif_ioctl(&ctrl->object, 89 "control pstate attr vers %d state %d index %d\n", 90 args->v0.version, args->v0.state, args->v0.index); 91 if (!clk) 92 return -ENODEV; 93 if (args->v0.state < NVIF_CONTROL_PSTATE_ATTR_V0_STATE_CURRENT) 94 return -EINVAL; 95 if (args->v0.state >= clk->state_nr) 96 return -EINVAL; 97 } else 98 return ret; 99 domain = clk->domains; 100 101 while (domain->name != nv_clk_src_max) { 102 if (domain->mname && ++j == args->v0.index) 103 break; 104 domain++; 105 } 106 107 if (domain->name == nv_clk_src_max) 108 return -EINVAL; 109 110 if (args->v0.state != NVIF_CONTROL_PSTATE_ATTR_V0_STATE_CURRENT) { 111 list_for_each_entry(pstate, &clk->states, head) { 112 if (i++ == args->v0.state) 113 break; 114 } 115 116 lo = pstate->base.domain[domain->name]; 117 hi = lo; 118 list_for_each_entry(cstate, &pstate->list, head) { 119 lo = min(lo, cstate->domain[domain->name]); 120 hi = max(hi, cstate->domain[domain->name]); 121 } 122 123 args->v0.state = pstate->pstate; 124 } else { 125 lo = max(nvkm_clk_read(clk, domain->name), 0); 126 hi = lo; 127 } 128 129 snprintf(args->v0.name, sizeof(args->v0.name), "%s", domain->mname); 130 snprintf(args->v0.unit, sizeof(args->v0.unit), "MHz"); 131 args->v0.min = lo / domain->mdiv; 132 args->v0.max = hi / domain->mdiv; 133 134 args->v0.index = 0; 135 while ((++domain)->name != nv_clk_src_max) { 136 if (domain->mname) { 137 args->v0.index = ++j; 138 break; 139 } 140 } 141 142 return 0; 143 } 144 145 static int 146 nvkm_control_mthd_pstate_user(struct nvkm_control *ctrl, void *data, u32 size) 147 { 148 union { 149 struct nvif_control_pstate_user_v0 v0; 150 } *args = data; 151 struct nvkm_clk *clk = ctrl->device->clk; 152 int ret = -ENOSYS; 153 154 nvif_ioctl(&ctrl->object, "control pstate user size %d\n", size); 155 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { 156 nvif_ioctl(&ctrl->object, 157 "control pstate user vers %d ustate %d pwrsrc %d\n", 158 args->v0.version, args->v0.ustate, args->v0.pwrsrc); 159 if (!clk) 160 return -ENODEV; 161 } else 162 return ret; 163 164 if (args->v0.pwrsrc >= 0) { 165 ret |= nvkm_clk_ustate(clk, args->v0.ustate, args->v0.pwrsrc); 166 } else { 167 ret |= nvkm_clk_ustate(clk, args->v0.ustate, 0); 168 ret |= nvkm_clk_ustate(clk, args->v0.ustate, 1); 169 } 170 171 return ret; 172 } 173 174 static int 175 nvkm_control_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) 176 { 177 struct nvkm_control *ctrl = nvkm_control(object); 178 switch (mthd) { 179 case NVIF_CONTROL_PSTATE_INFO: 180 return nvkm_control_mthd_pstate_info(ctrl, data, size); 181 case NVIF_CONTROL_PSTATE_ATTR: 182 return nvkm_control_mthd_pstate_attr(ctrl, data, size); 183 case NVIF_CONTROL_PSTATE_USER: 184 return nvkm_control_mthd_pstate_user(ctrl, data, size); 185 default: 186 break; 187 } 188 return -EINVAL; 189 } 190 191 static const struct nvkm_object_func 192 nvkm_control = { 193 .mthd = nvkm_control_mthd, 194 }; 195 196 static int 197 nvkm_control_new(struct nvkm_device *device, const struct nvkm_oclass *oclass, 198 void *data, u32 size, struct nvkm_object **pobject) 199 { 200 struct nvkm_control *ctrl; 201 202 if (!(ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL))) 203 return -ENOMEM; 204 *pobject = &ctrl->object; 205 ctrl->device = device; 206 207 nvkm_object_ctor(&nvkm_control, oclass, &ctrl->object); 208 return 0; 209 } 210 211 const struct nvkm_device_oclass 212 nvkm_control_oclass = { 213 .base.oclass = NVIF_CLASS_CONTROL, 214 .base.minver = -1, 215 .base.maxver = -1, 216 .ctor = nvkm_control_new, 217 }; 218