0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024 #include "priv.h"
0025
0026 #include <subdev/bios.h>
0027 #include <subdev/bios/vmap.h>
0028 #include <subdev/bios/volt.h>
0029 #include <subdev/therm.h>
0030
0031 int
0032 nvkm_volt_get(struct nvkm_volt *volt)
0033 {
0034 int ret, i;
0035
0036 if (volt->func->volt_get)
0037 return volt->func->volt_get(volt);
0038
0039 ret = volt->func->vid_get(volt);
0040 if (ret >= 0) {
0041 for (i = 0; i < volt->vid_nr; i++) {
0042 if (volt->vid[i].vid == ret)
0043 return volt->vid[i].uv;
0044 }
0045 ret = -EINVAL;
0046 }
0047 return ret;
0048 }
0049
0050 static int
0051 nvkm_volt_set(struct nvkm_volt *volt, u32 uv)
0052 {
0053 struct nvkm_subdev *subdev = &volt->subdev;
0054 int i, ret = -EINVAL, best_err = volt->max_uv, best = -1;
0055
0056 if (volt->func->volt_set)
0057 return volt->func->volt_set(volt, uv);
0058
0059 for (i = 0; i < volt->vid_nr; i++) {
0060 int err = volt->vid[i].uv - uv;
0061 if (err < 0 || err > best_err)
0062 continue;
0063
0064 best_err = err;
0065 best = i;
0066 if (best_err == 0)
0067 break;
0068 }
0069
0070 if (best == -1) {
0071 nvkm_error(subdev, "couldn't set %iuv\n", uv);
0072 return ret;
0073 }
0074
0075 ret = volt->func->vid_set(volt, volt->vid[best].vid);
0076 nvkm_debug(subdev, "set req %duv to %duv: %d\n", uv,
0077 volt->vid[best].uv, ret);
0078 return ret;
0079 }
0080
0081 int
0082 nvkm_volt_map_min(struct nvkm_volt *volt, u8 id)
0083 {
0084 struct nvkm_bios *bios = volt->subdev.device->bios;
0085 struct nvbios_vmap_entry info;
0086 u8 ver, len;
0087 u32 vmap;
0088
0089 vmap = nvbios_vmap_entry_parse(bios, id, &ver, &len, &info);
0090 if (vmap) {
0091 if (info.link != 0xff) {
0092 int ret = nvkm_volt_map_min(volt, info.link);
0093 if (ret < 0)
0094 return ret;
0095 info.min += ret;
0096 }
0097 return info.min;
0098 }
0099
0100 return id ? id * 10000 : -ENODEV;
0101 }
0102
0103 int
0104 nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temp)
0105 {
0106 struct nvkm_bios *bios = volt->subdev.device->bios;
0107 struct nvbios_vmap_entry info;
0108 u8 ver, len;
0109 u32 vmap;
0110
0111 vmap = nvbios_vmap_entry_parse(bios, id, &ver, &len, &info);
0112 if (vmap) {
0113 s64 result;
0114
0115 if (volt->speedo < 0)
0116 return volt->speedo;
0117
0118 if (ver == 0x10 || (ver == 0x20 && info.mode == 0)) {
0119 result = div64_s64((s64)info.arg[0], 10);
0120 result += div64_s64((s64)info.arg[1] * volt->speedo, 10);
0121 result += div64_s64((s64)info.arg[2] * volt->speedo * volt->speedo, 100000);
0122 } else if (ver == 0x20) {
0123 switch (info.mode) {
0124
0125 case 0x1:
0126 result = ((s64)info.arg[0] * 15625) >> 18;
0127 result += ((s64)info.arg[1] * volt->speedo * 15625) >> 18;
0128 result += ((s64)info.arg[2] * temp * 15625) >> 10;
0129 result += ((s64)info.arg[3] * volt->speedo * temp * 15625) >> 18;
0130 result += ((s64)info.arg[4] * volt->speedo * volt->speedo * 15625) >> 30;
0131 result += ((s64)info.arg[5] * temp * temp * 15625) >> 18;
0132 break;
0133 case 0x3:
0134 result = (info.min + info.max) / 2;
0135 break;
0136 case 0x2:
0137 default:
0138 result = info.min;
0139 break;
0140 }
0141 } else {
0142 return -ENODEV;
0143 }
0144
0145 result = min(max(result, (s64)info.min), (s64)info.max);
0146
0147 if (info.link != 0xff) {
0148 int ret = nvkm_volt_map(volt, info.link, temp);
0149 if (ret < 0)
0150 return ret;
0151 result += ret;
0152 }
0153 return result;
0154 }
0155
0156 return id ? id * 10000 : -ENODEV;
0157 }
0158
0159 int
0160 nvkm_volt_set_id(struct nvkm_volt *volt, u8 id, u8 min_id, u8 temp,
0161 int condition)
0162 {
0163 int ret;
0164
0165 if (volt->func->set_id)
0166 return volt->func->set_id(volt, id, condition);
0167
0168 ret = nvkm_volt_map(volt, id, temp);
0169 if (ret >= 0) {
0170 int prev = nvkm_volt_get(volt);
0171 if (!condition || prev < 0 ||
0172 (condition < 0 && ret < prev) ||
0173 (condition > 0 && ret > prev)) {
0174 int min = nvkm_volt_map(volt, min_id, temp);
0175 if (min >= 0)
0176 ret = max(min, ret);
0177 ret = nvkm_volt_set(volt, ret);
0178 } else {
0179 ret = 0;
0180 }
0181 }
0182 return ret;
0183 }
0184
0185 static void
0186 nvkm_volt_parse_bios(struct nvkm_bios *bios, struct nvkm_volt *volt)
0187 {
0188 struct nvkm_subdev *subdev = &bios->subdev;
0189 struct nvbios_volt_entry ivid;
0190 struct nvbios_volt info;
0191 u8 ver, hdr, cnt, len;
0192 u32 data;
0193 int i;
0194
0195 data = nvbios_volt_parse(bios, &ver, &hdr, &cnt, &len, &info);
0196 if (data && info.vidmask && info.base && info.step && info.ranged) {
0197 nvkm_debug(subdev, "found ranged based VIDs\n");
0198 volt->min_uv = info.min;
0199 volt->max_uv = info.max;
0200 for (i = 0; i < info.vidmask + 1; i++) {
0201 if (info.base >= info.min &&
0202 info.base <= info.max) {
0203 volt->vid[volt->vid_nr].uv = info.base;
0204 volt->vid[volt->vid_nr].vid = i;
0205 volt->vid_nr++;
0206 }
0207 info.base += info.step;
0208 }
0209 volt->vid_mask = info.vidmask;
0210 } else if (data && info.vidmask && !info.ranged) {
0211 nvkm_debug(subdev, "found entry based VIDs\n");
0212 volt->min_uv = 0xffffffff;
0213 volt->max_uv = 0;
0214 for (i = 0; i < cnt; i++) {
0215 data = nvbios_volt_entry_parse(bios, i, &ver, &hdr,
0216 &ivid);
0217 if (data) {
0218 volt->vid[volt->vid_nr].uv = ivid.voltage;
0219 volt->vid[volt->vid_nr].vid = ivid.vid;
0220 volt->vid_nr++;
0221 volt->min_uv = min(volt->min_uv, ivid.voltage);
0222 volt->max_uv = max(volt->max_uv, ivid.voltage);
0223 }
0224 }
0225 volt->vid_mask = info.vidmask;
0226 } else if (data && info.type == NVBIOS_VOLT_PWM) {
0227 volt->min_uv = info.base;
0228 volt->max_uv = info.base + info.pwm_range;
0229 }
0230 }
0231
0232 static int
0233 nvkm_volt_speedo_read(struct nvkm_volt *volt)
0234 {
0235 if (volt->func->speedo_read)
0236 return volt->func->speedo_read(volt);
0237 return -EINVAL;
0238 }
0239
0240 static int
0241 nvkm_volt_init(struct nvkm_subdev *subdev)
0242 {
0243 struct nvkm_volt *volt = nvkm_volt(subdev);
0244 int ret = nvkm_volt_get(volt);
0245 if (ret < 0) {
0246 if (ret != -ENODEV)
0247 nvkm_debug(subdev, "current voltage unknown\n");
0248 return 0;
0249 }
0250 nvkm_debug(subdev, "current voltage: %duv\n", ret);
0251 return 0;
0252 }
0253
0254 static int
0255 nvkm_volt_oneinit(struct nvkm_subdev *subdev)
0256 {
0257 struct nvkm_volt *volt = nvkm_volt(subdev);
0258
0259 volt->speedo = nvkm_volt_speedo_read(volt);
0260 if (volt->speedo > 0)
0261 nvkm_debug(&volt->subdev, "speedo %x\n", volt->speedo);
0262
0263 if (volt->func->oneinit)
0264 return volt->func->oneinit(volt);
0265
0266 return 0;
0267 }
0268
0269 static void *
0270 nvkm_volt_dtor(struct nvkm_subdev *subdev)
0271 {
0272 return nvkm_volt(subdev);
0273 }
0274
0275 static const struct nvkm_subdev_func
0276 nvkm_volt = {
0277 .dtor = nvkm_volt_dtor,
0278 .init = nvkm_volt_init,
0279 .oneinit = nvkm_volt_oneinit,
0280 };
0281
0282 void
0283 nvkm_volt_ctor(const struct nvkm_volt_func *func, struct nvkm_device *device,
0284 enum nvkm_subdev_type type, int inst, struct nvkm_volt *volt)
0285 {
0286 struct nvkm_bios *bios = device->bios;
0287 int i;
0288
0289 nvkm_subdev_ctor(&nvkm_volt, device, type, inst, &volt->subdev);
0290 volt->func = func;
0291
0292
0293 if (bios) {
0294 u8 ver, hdr, cnt, len;
0295 struct nvbios_vmap vmap;
0296
0297 nvkm_volt_parse_bios(bios, volt);
0298 nvkm_debug(&volt->subdev, "min: %iuv max: %iuv\n",
0299 volt->min_uv, volt->max_uv);
0300
0301 if (nvbios_vmap_parse(bios, &ver, &hdr, &cnt, &len, &vmap)) {
0302 volt->max0_id = vmap.max0;
0303 volt->max1_id = vmap.max1;
0304 volt->max2_id = vmap.max2;
0305 } else {
0306 volt->max0_id = 0xff;
0307 volt->max1_id = 0xff;
0308 volt->max2_id = 0xff;
0309 }
0310 }
0311
0312 if (volt->vid_nr) {
0313 for (i = 0; i < volt->vid_nr; i++) {
0314 nvkm_debug(&volt->subdev, "VID %02x: %duv\n",
0315 volt->vid[i].vid, volt->vid[i].uv);
0316 }
0317 }
0318 }
0319
0320 int
0321 nvkm_volt_new_(const struct nvkm_volt_func *func, struct nvkm_device *device,
0322 enum nvkm_subdev_type type, int inst, struct nvkm_volt **pvolt)
0323 {
0324 if (!(*pvolt = kzalloc(sizeof(**pvolt), GFP_KERNEL)))
0325 return -ENOMEM;
0326 nvkm_volt_ctor(func, device, type, inst, *pvolt);
0327 return 0;
0328 }