1f3867f43SBen Skeggs /* 2f3867f43SBen Skeggs * Copyright 2013 Red Hat Inc. 3f3867f43SBen Skeggs * 4f3867f43SBen Skeggs * Permission is hereby granted, free of charge, to any person obtaining a 5f3867f43SBen Skeggs * copy of this software and associated documentation files (the "Software"), 6f3867f43SBen Skeggs * to deal in the Software without restriction, including without limitation 7f3867f43SBen Skeggs * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8f3867f43SBen Skeggs * and/or sell copies of the Software, and to permit persons to whom the 9f3867f43SBen Skeggs * Software is furnished to do so, subject to the following conditions: 10f3867f43SBen Skeggs * 11f3867f43SBen Skeggs * The above copyright notice and this permission notice shall be included in 12f3867f43SBen Skeggs * all copies or substantial portions of the Software. 13f3867f43SBen Skeggs * 14f3867f43SBen Skeggs * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15f3867f43SBen Skeggs * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16f3867f43SBen Skeggs * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17f3867f43SBen Skeggs * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18f3867f43SBen Skeggs * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19f3867f43SBen Skeggs * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20f3867f43SBen Skeggs * OTHER DEALINGS IN THE SOFTWARE. 21f3867f43SBen Skeggs * 22f3867f43SBen Skeggs * Authors: Ben Skeggs 23f3867f43SBen Skeggs */ 246625f55cSBen Skeggs #include "priv.h" 256625f55cSBen Skeggs 26f3867f43SBen Skeggs #include <subdev/bios.h> 27f3867f43SBen Skeggs #include <subdev/bios/boost.h> 28f3867f43SBen Skeggs #include <subdev/bios/cstep.h> 29f3867f43SBen Skeggs #include <subdev/bios/perf.h> 307632b30eSBen Skeggs #include <subdev/fb.h> 317632b30eSBen Skeggs #include <subdev/therm.h> 327632b30eSBen Skeggs #include <subdev/volt.h> 337632b30eSBen Skeggs 347632b30eSBen Skeggs #include <core/option.h> 35f3867f43SBen Skeggs 36f3867f43SBen Skeggs /****************************************************************************** 37f3867f43SBen Skeggs * misc 38f3867f43SBen Skeggs *****************************************************************************/ 39f3867f43SBen Skeggs static u32 407632b30eSBen Skeggs nvkm_clk_adjust(struct nvkm_clk *clk, bool adjust, 41f3867f43SBen Skeggs u8 pstate, u8 domain, u32 input) 42f3867f43SBen Skeggs { 4346484438SBen Skeggs struct nvkm_bios *bios = clk->subdev.device->bios; 44f3867f43SBen Skeggs struct nvbios_boostE boostE; 45f3867f43SBen Skeggs u8 ver, hdr, cnt, len; 46f3867f43SBen Skeggs u16 data; 47f3867f43SBen Skeggs 48f3867f43SBen Skeggs data = nvbios_boostEm(bios, pstate, &ver, &hdr, &cnt, &len, &boostE); 49f3867f43SBen Skeggs if (data) { 50f3867f43SBen Skeggs struct nvbios_boostS boostS; 51f3867f43SBen Skeggs u8 idx = 0, sver, shdr; 52f3867f43SBen Skeggs u16 subd; 53f3867f43SBen Skeggs 54f3867f43SBen Skeggs input = max(boostE.min, input); 55f3867f43SBen Skeggs input = min(boostE.max, input); 56f3867f43SBen Skeggs do { 57f3867f43SBen Skeggs sver = ver; 58f3867f43SBen Skeggs shdr = hdr; 59f3867f43SBen Skeggs subd = nvbios_boostSp(bios, idx++, data, &sver, &shdr, 60f3867f43SBen Skeggs cnt, len, &boostS); 61f3867f43SBen Skeggs if (subd && boostS.domain == domain) { 62f3867f43SBen Skeggs if (adjust) 63f3867f43SBen Skeggs input = input * boostS.percent / 100; 64f3867f43SBen Skeggs input = max(boostS.min, input); 65f3867f43SBen Skeggs input = min(boostS.max, input); 66f3867f43SBen Skeggs break; 67f3867f43SBen Skeggs } 68f3867f43SBen Skeggs } while (subd); 69f3867f43SBen Skeggs } 70f3867f43SBen Skeggs 71f3867f43SBen Skeggs return input; 72f3867f43SBen Skeggs } 73f3867f43SBen Skeggs 74f3867f43SBen Skeggs /****************************************************************************** 75f3867f43SBen Skeggs * C-States 76f3867f43SBen Skeggs *****************************************************************************/ 77f3867f43SBen Skeggs static int 787632b30eSBen Skeggs nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) 79f3867f43SBen Skeggs { 80b907649eSBen Skeggs struct nvkm_subdev *subdev = &clk->subdev; 81b907649eSBen Skeggs struct nvkm_device *device = subdev->device; 82b907649eSBen Skeggs struct nvkm_therm *therm = device->therm; 83b907649eSBen Skeggs struct nvkm_volt *volt = device->volt; 847632b30eSBen Skeggs struct nvkm_cstate *cstate; 85f3867f43SBen Skeggs int ret; 86f3867f43SBen Skeggs 87f3867f43SBen Skeggs if (!list_empty(&pstate->list)) { 88f3867f43SBen Skeggs cstate = list_entry(pstate->list.prev, typeof(*cstate), head); 89f3867f43SBen Skeggs } else { 90f3867f43SBen Skeggs cstate = &pstate->base; 91f3867f43SBen Skeggs } 92f3867f43SBen Skeggs 93da06b46bSBen Skeggs if (therm) { 94da06b46bSBen Skeggs ret = nvkm_therm_cstate(therm, pstate->fanspeed, +1); 95f3867f43SBen Skeggs if (ret && ret != -ENODEV) { 96b907649eSBen Skeggs nvkm_error(subdev, "failed to raise fan speed: %d\n", ret); 97f3867f43SBen Skeggs return ret; 98f3867f43SBen Skeggs } 99f3867f43SBen Skeggs } 100f3867f43SBen Skeggs 101f3867f43SBen Skeggs if (volt) { 102f3867f43SBen Skeggs ret = volt->set_id(volt, cstate->voltage, +1); 103f3867f43SBen Skeggs if (ret && ret != -ENODEV) { 104b907649eSBen Skeggs nvkm_error(subdev, "failed to raise voltage: %d\n", ret); 105f3867f43SBen Skeggs return ret; 106f3867f43SBen Skeggs } 107f3867f43SBen Skeggs } 108f3867f43SBen Skeggs 1096625f55cSBen Skeggs ret = clk->func->calc(clk, cstate); 110f3867f43SBen Skeggs if (ret == 0) { 1116625f55cSBen Skeggs ret = clk->func->prog(clk); 1126625f55cSBen Skeggs clk->func->tidy(clk); 113f3867f43SBen Skeggs } 114f3867f43SBen Skeggs 115f3867f43SBen Skeggs if (volt) { 116f3867f43SBen Skeggs ret = volt->set_id(volt, cstate->voltage, -1); 117f3867f43SBen Skeggs if (ret && ret != -ENODEV) 118b907649eSBen Skeggs nvkm_error(subdev, "failed to lower voltage: %d\n", ret); 119f3867f43SBen Skeggs } 120f3867f43SBen Skeggs 121da06b46bSBen Skeggs if (therm) { 122da06b46bSBen Skeggs ret = nvkm_therm_cstate(therm, pstate->fanspeed, -1); 123f3867f43SBen Skeggs if (ret && ret != -ENODEV) 124b907649eSBen Skeggs nvkm_error(subdev, "failed to lower fan speed: %d\n", ret); 125f3867f43SBen Skeggs } 126f3867f43SBen Skeggs 1273eca809bSBen Skeggs return ret; 128f3867f43SBen Skeggs } 129f3867f43SBen Skeggs 130f3867f43SBen Skeggs static void 1317632b30eSBen Skeggs nvkm_cstate_del(struct nvkm_cstate *cstate) 132f3867f43SBen Skeggs { 133f3867f43SBen Skeggs list_del(&cstate->head); 134f3867f43SBen Skeggs kfree(cstate); 135f3867f43SBen Skeggs } 136f3867f43SBen Skeggs 137f3867f43SBen Skeggs static int 1387632b30eSBen Skeggs nvkm_cstate_new(struct nvkm_clk *clk, int idx, struct nvkm_pstate *pstate) 139f3867f43SBen Skeggs { 14046484438SBen Skeggs struct nvkm_bios *bios = clk->subdev.device->bios; 1416625f55cSBen Skeggs const struct nvkm_domain *domain = clk->domains; 1427632b30eSBen Skeggs struct nvkm_cstate *cstate = NULL; 143f3867f43SBen Skeggs struct nvbios_cstepX cstepX; 144f3867f43SBen Skeggs u8 ver, hdr; 145f3867f43SBen Skeggs u16 data; 146f3867f43SBen Skeggs 147f3867f43SBen Skeggs data = nvbios_cstepXp(bios, idx, &ver, &hdr, &cstepX); 148f3867f43SBen Skeggs if (!data) 149f3867f43SBen Skeggs return -ENOENT; 150f3867f43SBen Skeggs 151f3867f43SBen Skeggs cstate = kzalloc(sizeof(*cstate), GFP_KERNEL); 152f3867f43SBen Skeggs if (!cstate) 153f3867f43SBen Skeggs return -ENOMEM; 154f3867f43SBen Skeggs 155f3867f43SBen Skeggs *cstate = pstate->base; 156f3867f43SBen Skeggs cstate->voltage = cstepX.voltage; 157f3867f43SBen Skeggs 158f3867f43SBen Skeggs while (domain && domain->name != nv_clk_src_max) { 159f3867f43SBen Skeggs if (domain->flags & NVKM_CLK_DOM_FLAG_CORE) { 1607632b30eSBen Skeggs u32 freq = nvkm_clk_adjust(clk, true, pstate->pstate, 1617632b30eSBen Skeggs domain->bios, cstepX.freq); 162f3867f43SBen Skeggs cstate->domain[domain->name] = freq; 163f3867f43SBen Skeggs } 164f3867f43SBen Skeggs domain++; 165f3867f43SBen Skeggs } 166f3867f43SBen Skeggs 167f3867f43SBen Skeggs list_add(&cstate->head, &pstate->list); 168f3867f43SBen Skeggs return 0; 169f3867f43SBen Skeggs } 170f3867f43SBen Skeggs 171f3867f43SBen Skeggs /****************************************************************************** 172f3867f43SBen Skeggs * P-States 173f3867f43SBen Skeggs *****************************************************************************/ 174f3867f43SBen Skeggs static int 1757632b30eSBen Skeggs nvkm_pstate_prog(struct nvkm_clk *clk, int pstatei) 176f3867f43SBen Skeggs { 177b907649eSBen Skeggs struct nvkm_subdev *subdev = &clk->subdev; 178d36a99d2SBen Skeggs struct nvkm_ram *ram = subdev->device->fb->ram; 1797632b30eSBen Skeggs struct nvkm_pstate *pstate; 180f3867f43SBen Skeggs int ret, idx = 0; 181f3867f43SBen Skeggs 182f3867f43SBen Skeggs list_for_each_entry(pstate, &clk->states, head) { 183f3867f43SBen Skeggs if (idx++ == pstatei) 184f3867f43SBen Skeggs break; 185f3867f43SBen Skeggs } 186f3867f43SBen Skeggs 187b907649eSBen Skeggs nvkm_debug(subdev, "setting performance state %d\n", pstatei); 188f3867f43SBen Skeggs clk->pstate = pstatei; 189f3867f43SBen Skeggs 190d36a99d2SBen Skeggs if (ram && ram->func->calc) { 191f3867f43SBen Skeggs int khz = pstate->base.domain[nv_clk_src_mem]; 192f3867f43SBen Skeggs do { 193d36a99d2SBen Skeggs ret = ram->func->calc(ram, khz); 194f3867f43SBen Skeggs if (ret == 0) 195d36a99d2SBen Skeggs ret = ram->func->prog(ram); 196f3867f43SBen Skeggs } while (ret > 0); 197d36a99d2SBen Skeggs ram->func->tidy(ram); 198f3867f43SBen Skeggs } 199f3867f43SBen Skeggs 2007632b30eSBen Skeggs return nvkm_cstate_prog(clk, pstate, 0); 201f3867f43SBen Skeggs } 202f3867f43SBen Skeggs 203f3867f43SBen Skeggs static void 2047632b30eSBen Skeggs nvkm_pstate_work(struct work_struct *work) 205f3867f43SBen Skeggs { 2067632b30eSBen Skeggs struct nvkm_clk *clk = container_of(work, typeof(*clk), work); 207b907649eSBen Skeggs struct nvkm_subdev *subdev = &clk->subdev; 208f3867f43SBen Skeggs int pstate; 209f3867f43SBen Skeggs 210f3867f43SBen Skeggs if (!atomic_xchg(&clk->waiting, 0)) 211f3867f43SBen Skeggs return; 212f3867f43SBen Skeggs clk->pwrsrc = power_supply_is_system_supplied(); 213f3867f43SBen Skeggs 214b907649eSBen Skeggs nvkm_trace(subdev, "P %d PWR %d U(AC) %d U(DC) %d A %d T %d D %d\n", 215f3867f43SBen Skeggs clk->pstate, clk->pwrsrc, clk->ustate_ac, clk->ustate_dc, 216f3867f43SBen Skeggs clk->astate, clk->tstate, clk->dstate); 217f3867f43SBen Skeggs 218f3867f43SBen Skeggs pstate = clk->pwrsrc ? clk->ustate_ac : clk->ustate_dc; 219f3867f43SBen Skeggs if (clk->state_nr && pstate != -1) { 220f3867f43SBen Skeggs pstate = (pstate < 0) ? clk->astate : pstate; 22185fa319dSWei Ni pstate = min(pstate, clk->state_nr - 1 + clk->tstate); 222f3867f43SBen Skeggs pstate = max(pstate, clk->dstate); 223f3867f43SBen Skeggs } else { 224f3867f43SBen Skeggs pstate = clk->pstate = -1; 225f3867f43SBen Skeggs } 226f3867f43SBen Skeggs 227b907649eSBen Skeggs nvkm_trace(subdev, "-> %d\n", pstate); 228f3867f43SBen Skeggs if (pstate != clk->pstate) { 2297632b30eSBen Skeggs int ret = nvkm_pstate_prog(clk, pstate); 230f3867f43SBen Skeggs if (ret) { 231b907649eSBen Skeggs nvkm_error(subdev, "error setting pstate %d: %d\n", 232f3867f43SBen Skeggs pstate, ret); 233f3867f43SBen Skeggs } 234f3867f43SBen Skeggs } 235f3867f43SBen Skeggs 236f3867f43SBen Skeggs wake_up_all(&clk->wait); 237f3867f43SBen Skeggs nvkm_notify_get(&clk->pwrsrc_ntfy); 238f3867f43SBen Skeggs } 239f3867f43SBen Skeggs 240f3867f43SBen Skeggs static int 2417632b30eSBen Skeggs nvkm_pstate_calc(struct nvkm_clk *clk, bool wait) 242f3867f43SBen Skeggs { 243f3867f43SBen Skeggs atomic_set(&clk->waiting, 1); 244f3867f43SBen Skeggs schedule_work(&clk->work); 245f3867f43SBen Skeggs if (wait) 246f3867f43SBen Skeggs wait_event(clk->wait, !atomic_read(&clk->waiting)); 247f3867f43SBen Skeggs return 0; 248f3867f43SBen Skeggs } 249f3867f43SBen Skeggs 250f3867f43SBen Skeggs static void 2517632b30eSBen Skeggs nvkm_pstate_info(struct nvkm_clk *clk, struct nvkm_pstate *pstate) 252f3867f43SBen Skeggs { 2536625f55cSBen Skeggs const struct nvkm_domain *clock = clk->domains - 1; 2547632b30eSBen Skeggs struct nvkm_cstate *cstate; 255b907649eSBen Skeggs struct nvkm_subdev *subdev = &clk->subdev; 256f3867f43SBen Skeggs char info[3][32] = { "", "", "" }; 257f3867f43SBen Skeggs char name[4] = "--"; 258f3867f43SBen Skeggs int i = -1; 259f3867f43SBen Skeggs 260f3867f43SBen Skeggs if (pstate->pstate != 0xff) 261f3867f43SBen Skeggs snprintf(name, sizeof(name), "%02x", pstate->pstate); 262f3867f43SBen Skeggs 263f3867f43SBen Skeggs while ((++clock)->name != nv_clk_src_max) { 264f3867f43SBen Skeggs u32 lo = pstate->base.domain[clock->name]; 265f3867f43SBen Skeggs u32 hi = lo; 266f3867f43SBen Skeggs if (hi == 0) 267f3867f43SBen Skeggs continue; 268f3867f43SBen Skeggs 269b907649eSBen Skeggs nvkm_debug(subdev, "%02x: %10d KHz\n", clock->name, lo); 270f3867f43SBen Skeggs list_for_each_entry(cstate, &pstate->list, head) { 271f3867f43SBen Skeggs u32 freq = cstate->domain[clock->name]; 272f3867f43SBen Skeggs lo = min(lo, freq); 273f3867f43SBen Skeggs hi = max(hi, freq); 274b907649eSBen Skeggs nvkm_debug(subdev, "%10d KHz\n", freq); 275f3867f43SBen Skeggs } 276f3867f43SBen Skeggs 277f3867f43SBen Skeggs if (clock->mname && ++i < ARRAY_SIZE(info)) { 278f3867f43SBen Skeggs lo /= clock->mdiv; 279f3867f43SBen Skeggs hi /= clock->mdiv; 280f3867f43SBen Skeggs if (lo == hi) { 281f3867f43SBen Skeggs snprintf(info[i], sizeof(info[i]), "%s %d MHz", 282f3867f43SBen Skeggs clock->mname, lo); 283f3867f43SBen Skeggs } else { 284f3867f43SBen Skeggs snprintf(info[i], sizeof(info[i]), 285f3867f43SBen Skeggs "%s %d-%d MHz", clock->mname, lo, hi); 286f3867f43SBen Skeggs } 287f3867f43SBen Skeggs } 288f3867f43SBen Skeggs } 289f3867f43SBen Skeggs 290b907649eSBen Skeggs nvkm_debug(subdev, "%s: %s %s %s\n", name, info[0], info[1], info[2]); 291f3867f43SBen Skeggs } 292f3867f43SBen Skeggs 293f3867f43SBen Skeggs static void 2947632b30eSBen Skeggs nvkm_pstate_del(struct nvkm_pstate *pstate) 295f3867f43SBen Skeggs { 2967632b30eSBen Skeggs struct nvkm_cstate *cstate, *temp; 297f3867f43SBen Skeggs 298f3867f43SBen Skeggs list_for_each_entry_safe(cstate, temp, &pstate->list, head) { 2997632b30eSBen Skeggs nvkm_cstate_del(cstate); 300f3867f43SBen Skeggs } 301f3867f43SBen Skeggs 302f3867f43SBen Skeggs list_del(&pstate->head); 303f3867f43SBen Skeggs kfree(pstate); 304f3867f43SBen Skeggs } 305f3867f43SBen Skeggs 306f3867f43SBen Skeggs static int 3077632b30eSBen Skeggs nvkm_pstate_new(struct nvkm_clk *clk, int idx) 308f3867f43SBen Skeggs { 30946484438SBen Skeggs struct nvkm_bios *bios = clk->subdev.device->bios; 3106625f55cSBen Skeggs const struct nvkm_domain *domain = clk->domains - 1; 3117632b30eSBen Skeggs struct nvkm_pstate *pstate; 3127632b30eSBen Skeggs struct nvkm_cstate *cstate; 313f3867f43SBen Skeggs struct nvbios_cstepE cstepE; 314f3867f43SBen Skeggs struct nvbios_perfE perfE; 315f3867f43SBen Skeggs u8 ver, hdr, cnt, len; 316f3867f43SBen Skeggs u16 data; 317f3867f43SBen Skeggs 318f3867f43SBen Skeggs data = nvbios_perfEp(bios, idx, &ver, &hdr, &cnt, &len, &perfE); 319f3867f43SBen Skeggs if (!data) 320f3867f43SBen Skeggs return -EINVAL; 321f3867f43SBen Skeggs if (perfE.pstate == 0xff) 322f3867f43SBen Skeggs return 0; 323f3867f43SBen Skeggs 324f3867f43SBen Skeggs pstate = kzalloc(sizeof(*pstate), GFP_KERNEL); 325f3867f43SBen Skeggs cstate = &pstate->base; 326f3867f43SBen Skeggs if (!pstate) 327f3867f43SBen Skeggs return -ENOMEM; 328f3867f43SBen Skeggs 329f3867f43SBen Skeggs INIT_LIST_HEAD(&pstate->list); 330f3867f43SBen Skeggs 331f3867f43SBen Skeggs pstate->pstate = perfE.pstate; 332f3867f43SBen Skeggs pstate->fanspeed = perfE.fanspeed; 333f3867f43SBen Skeggs cstate->voltage = perfE.voltage; 334f3867f43SBen Skeggs cstate->domain[nv_clk_src_core] = perfE.core; 335f3867f43SBen Skeggs cstate->domain[nv_clk_src_shader] = perfE.shader; 336f3867f43SBen Skeggs cstate->domain[nv_clk_src_mem] = perfE.memory; 337f3867f43SBen Skeggs cstate->domain[nv_clk_src_vdec] = perfE.vdec; 338f3867f43SBen Skeggs cstate->domain[nv_clk_src_dom6] = perfE.disp; 339f3867f43SBen Skeggs 340f3867f43SBen Skeggs while (ver >= 0x40 && (++domain)->name != nv_clk_src_max) { 341f3867f43SBen Skeggs struct nvbios_perfS perfS; 342f3867f43SBen Skeggs u8 sver = ver, shdr = hdr; 343f3867f43SBen Skeggs u32 perfSe = nvbios_perfSp(bios, data, domain->bios, 344f3867f43SBen Skeggs &sver, &shdr, cnt, len, &perfS); 345f3867f43SBen Skeggs if (perfSe == 0 || sver != 0x40) 346f3867f43SBen Skeggs continue; 347f3867f43SBen Skeggs 348f3867f43SBen Skeggs if (domain->flags & NVKM_CLK_DOM_FLAG_CORE) { 3497632b30eSBen Skeggs perfS.v40.freq = nvkm_clk_adjust(clk, false, 350f3867f43SBen Skeggs pstate->pstate, 351f3867f43SBen Skeggs domain->bios, 352f3867f43SBen Skeggs perfS.v40.freq); 353f3867f43SBen Skeggs } 354f3867f43SBen Skeggs 355f3867f43SBen Skeggs cstate->domain[domain->name] = perfS.v40.freq; 356f3867f43SBen Skeggs } 357f3867f43SBen Skeggs 358f3867f43SBen Skeggs data = nvbios_cstepEm(bios, pstate->pstate, &ver, &hdr, &cstepE); 359f3867f43SBen Skeggs if (data) { 360f3867f43SBen Skeggs int idx = cstepE.index; 361f3867f43SBen Skeggs do { 3627632b30eSBen Skeggs nvkm_cstate_new(clk, idx, pstate); 363f3867f43SBen Skeggs } while(idx--); 364f3867f43SBen Skeggs } 365f3867f43SBen Skeggs 3667632b30eSBen Skeggs nvkm_pstate_info(clk, pstate); 367f3867f43SBen Skeggs list_add_tail(&pstate->head, &clk->states); 368f3867f43SBen Skeggs clk->state_nr++; 369f3867f43SBen Skeggs return 0; 370f3867f43SBen Skeggs } 371f3867f43SBen Skeggs 372f3867f43SBen Skeggs /****************************************************************************** 373f3867f43SBen Skeggs * Adjustment triggers 374f3867f43SBen Skeggs *****************************************************************************/ 375f3867f43SBen Skeggs static int 3767632b30eSBen Skeggs nvkm_clk_ustate_update(struct nvkm_clk *clk, int req) 377f3867f43SBen Skeggs { 3787632b30eSBen Skeggs struct nvkm_pstate *pstate; 379f3867f43SBen Skeggs int i = 0; 380f3867f43SBen Skeggs 381f3867f43SBen Skeggs if (!clk->allow_reclock) 382f3867f43SBen Skeggs return -ENOSYS; 383f3867f43SBen Skeggs 384f3867f43SBen Skeggs if (req != -1 && req != -2) { 385f3867f43SBen Skeggs list_for_each_entry(pstate, &clk->states, head) { 386f3867f43SBen Skeggs if (pstate->pstate == req) 387f3867f43SBen Skeggs break; 388f3867f43SBen Skeggs i++; 389f3867f43SBen Skeggs } 390f3867f43SBen Skeggs 391f3867f43SBen Skeggs if (pstate->pstate != req) 392f3867f43SBen Skeggs return -EINVAL; 393f3867f43SBen Skeggs req = i; 394f3867f43SBen Skeggs } 395f3867f43SBen Skeggs 396f3867f43SBen Skeggs return req + 2; 397f3867f43SBen Skeggs } 398f3867f43SBen Skeggs 399f3867f43SBen Skeggs static int 4007632b30eSBen Skeggs nvkm_clk_nstate(struct nvkm_clk *clk, const char *mode, int arglen) 401f3867f43SBen Skeggs { 402f3867f43SBen Skeggs int ret = 1; 403f3867f43SBen Skeggs 404f3867f43SBen Skeggs if (clk->allow_reclock && !strncasecmpz(mode, "auto", arglen)) 405f3867f43SBen Skeggs return -2; 406f3867f43SBen Skeggs 407f3867f43SBen Skeggs if (strncasecmpz(mode, "disabled", arglen)) { 408f3867f43SBen Skeggs char save = mode[arglen]; 409f3867f43SBen Skeggs long v; 410f3867f43SBen Skeggs 411f3867f43SBen Skeggs ((char *)mode)[arglen] = '\0'; 412f3867f43SBen Skeggs if (!kstrtol(mode, 0, &v)) { 4137632b30eSBen Skeggs ret = nvkm_clk_ustate_update(clk, v); 414f3867f43SBen Skeggs if (ret < 0) 415f3867f43SBen Skeggs ret = 1; 416f3867f43SBen Skeggs } 417f3867f43SBen Skeggs ((char *)mode)[arglen] = save; 418f3867f43SBen Skeggs } 419f3867f43SBen Skeggs 420f3867f43SBen Skeggs return ret - 2; 421f3867f43SBen Skeggs } 422f3867f43SBen Skeggs 423f3867f43SBen Skeggs int 4247632b30eSBen Skeggs nvkm_clk_ustate(struct nvkm_clk *clk, int req, int pwr) 425f3867f43SBen Skeggs { 4267632b30eSBen Skeggs int ret = nvkm_clk_ustate_update(clk, req); 427f3867f43SBen Skeggs if (ret >= 0) { 428f3867f43SBen Skeggs if (ret -= 2, pwr) clk->ustate_ac = ret; 429f3867f43SBen Skeggs else clk->ustate_dc = ret; 4307632b30eSBen Skeggs return nvkm_pstate_calc(clk, true); 431f3867f43SBen Skeggs } 432f3867f43SBen Skeggs return ret; 433f3867f43SBen Skeggs } 434f3867f43SBen Skeggs 435f3867f43SBen Skeggs int 4367632b30eSBen Skeggs nvkm_clk_astate(struct nvkm_clk *clk, int req, int rel, bool wait) 437f3867f43SBen Skeggs { 438f3867f43SBen Skeggs if (!rel) clk->astate = req; 439f3867f43SBen Skeggs if ( rel) clk->astate += rel; 440f3867f43SBen Skeggs clk->astate = min(clk->astate, clk->state_nr - 1); 441f3867f43SBen Skeggs clk->astate = max(clk->astate, 0); 4427632b30eSBen Skeggs return nvkm_pstate_calc(clk, wait); 443f3867f43SBen Skeggs } 444f3867f43SBen Skeggs 445f3867f43SBen Skeggs int 4467632b30eSBen Skeggs nvkm_clk_tstate(struct nvkm_clk *clk, int req, int rel) 447f3867f43SBen Skeggs { 448f3867f43SBen Skeggs if (!rel) clk->tstate = req; 449f3867f43SBen Skeggs if ( rel) clk->tstate += rel; 450f3867f43SBen Skeggs clk->tstate = min(clk->tstate, 0); 451f3867f43SBen Skeggs clk->tstate = max(clk->tstate, -(clk->state_nr - 1)); 4527632b30eSBen Skeggs return nvkm_pstate_calc(clk, true); 453f3867f43SBen Skeggs } 454f3867f43SBen Skeggs 455f3867f43SBen Skeggs int 4567632b30eSBen Skeggs nvkm_clk_dstate(struct nvkm_clk *clk, int req, int rel) 457f3867f43SBen Skeggs { 458f3867f43SBen Skeggs if (!rel) clk->dstate = req; 459f3867f43SBen Skeggs if ( rel) clk->dstate += rel; 460f3867f43SBen Skeggs clk->dstate = min(clk->dstate, clk->state_nr - 1); 461f3867f43SBen Skeggs clk->dstate = max(clk->dstate, 0); 4627632b30eSBen Skeggs return nvkm_pstate_calc(clk, true); 463f3867f43SBen Skeggs } 464f3867f43SBen Skeggs 465f3867f43SBen Skeggs static int 4667632b30eSBen Skeggs nvkm_clk_pwrsrc(struct nvkm_notify *notify) 467f3867f43SBen Skeggs { 4687632b30eSBen Skeggs struct nvkm_clk *clk = 469f3867f43SBen Skeggs container_of(notify, typeof(*clk), pwrsrc_ntfy); 4707632b30eSBen Skeggs nvkm_pstate_calc(clk, false); 471f3867f43SBen Skeggs return NVKM_NOTIFY_DROP; 472f3867f43SBen Skeggs } 473f3867f43SBen Skeggs 474f3867f43SBen Skeggs /****************************************************************************** 475f3867f43SBen Skeggs * subdev base class implementation 476f3867f43SBen Skeggs *****************************************************************************/ 477f3867f43SBen Skeggs 478f3867f43SBen Skeggs int 4796625f55cSBen Skeggs nvkm_clk_read(struct nvkm_clk *clk, enum nv_clk_src src) 480f3867f43SBen Skeggs { 4816625f55cSBen Skeggs return clk->func->read(clk, src); 482f3867f43SBen Skeggs } 483f3867f43SBen Skeggs 4846625f55cSBen Skeggs static int 4856625f55cSBen Skeggs nvkm_clk_fini(struct nvkm_subdev *subdev, bool suspend) 486f3867f43SBen Skeggs { 4876625f55cSBen Skeggs struct nvkm_clk *clk = nvkm_clk(subdev); 4886625f55cSBen Skeggs nvkm_notify_put(&clk->pwrsrc_ntfy); 4896625f55cSBen Skeggs flush_work(&clk->work); 4906625f55cSBen Skeggs if (clk->func->fini) 4916625f55cSBen Skeggs clk->func->fini(clk); 4926625f55cSBen Skeggs return 0; 4936625f55cSBen Skeggs } 494f3867f43SBen Skeggs 4956625f55cSBen Skeggs static int 4966625f55cSBen Skeggs nvkm_clk_init(struct nvkm_subdev *subdev) 4976625f55cSBen Skeggs { 4986625f55cSBen Skeggs struct nvkm_clk *clk = nvkm_clk(subdev); 4996625f55cSBen Skeggs const struct nvkm_domain *clock = clk->domains; 5006625f55cSBen Skeggs int ret; 501f3867f43SBen Skeggs 502f3867f43SBen Skeggs memset(&clk->bstate, 0x00, sizeof(clk->bstate)); 503f3867f43SBen Skeggs INIT_LIST_HEAD(&clk->bstate.list); 504f3867f43SBen Skeggs clk->bstate.pstate = 0xff; 505f3867f43SBen Skeggs 506f3867f43SBen Skeggs while (clock->name != nv_clk_src_max) { 5076625f55cSBen Skeggs ret = nvkm_clk_read(clk, clock->name); 508f3867f43SBen Skeggs if (ret < 0) { 509b907649eSBen Skeggs nvkm_error(subdev, "%02x freq unknown\n", clock->name); 510f3867f43SBen Skeggs return ret; 511f3867f43SBen Skeggs } 512f3867f43SBen Skeggs clk->bstate.base.domain[clock->name] = ret; 513f3867f43SBen Skeggs clock++; 514f3867f43SBen Skeggs } 515f3867f43SBen Skeggs 5167632b30eSBen Skeggs nvkm_pstate_info(clk, &clk->bstate); 517f3867f43SBen Skeggs 5186625f55cSBen Skeggs if (clk->func->init) 5196625f55cSBen Skeggs return clk->func->init(clk); 5206625f55cSBen Skeggs 521f3867f43SBen Skeggs clk->astate = clk->state_nr - 1; 522f3867f43SBen Skeggs clk->tstate = 0; 523f3867f43SBen Skeggs clk->dstate = 0; 524f3867f43SBen Skeggs clk->pstate = -1; 5257632b30eSBen Skeggs nvkm_pstate_calc(clk, true); 526f3867f43SBen Skeggs return 0; 527f3867f43SBen Skeggs } 528f3867f43SBen Skeggs 5296625f55cSBen Skeggs static void * 5306625f55cSBen Skeggs nvkm_clk_dtor(struct nvkm_subdev *subdev) 531f3867f43SBen Skeggs { 5326625f55cSBen Skeggs struct nvkm_clk *clk = nvkm_clk(subdev); 5337632b30eSBen Skeggs struct nvkm_pstate *pstate, *temp; 534f3867f43SBen Skeggs 535f3867f43SBen Skeggs nvkm_notify_fini(&clk->pwrsrc_ntfy); 536f3867f43SBen Skeggs 5376625f55cSBen Skeggs /* Early return if the pstates have been provided statically */ 5386625f55cSBen Skeggs if (clk->func->pstates) 5396625f55cSBen Skeggs return clk; 5406625f55cSBen Skeggs 541f3867f43SBen Skeggs list_for_each_entry_safe(pstate, temp, &clk->states, head) { 5427632b30eSBen Skeggs nvkm_pstate_del(pstate); 543f3867f43SBen Skeggs } 544f3867f43SBen Skeggs 5456625f55cSBen Skeggs return clk; 546f3867f43SBen Skeggs } 547f3867f43SBen Skeggs 5486625f55cSBen Skeggs static const struct nvkm_subdev_func 5496625f55cSBen Skeggs nvkm_clk = { 5506625f55cSBen Skeggs .dtor = nvkm_clk_dtor, 5516625f55cSBen Skeggs .init = nvkm_clk_init, 5526625f55cSBen Skeggs .fini = nvkm_clk_fini, 5536625f55cSBen Skeggs }; 5546625f55cSBen Skeggs 555f3867f43SBen Skeggs int 5566625f55cSBen Skeggs nvkm_clk_ctor(const struct nvkm_clk_func *func, struct nvkm_device *device, 5576625f55cSBen Skeggs int index, bool allow_reclock, struct nvkm_clk *clk) 558f3867f43SBen Skeggs { 559f3867f43SBen Skeggs int ret, idx, arglen; 560f3867f43SBen Skeggs const char *mode; 561f3867f43SBen Skeggs 5626625f55cSBen Skeggs nvkm_subdev_ctor(&nvkm_clk, device, index, 0, &clk->subdev); 5636625f55cSBen Skeggs clk->func = func; 564f3867f43SBen Skeggs INIT_LIST_HEAD(&clk->states); 5656625f55cSBen Skeggs clk->domains = func->domains; 566f3867f43SBen Skeggs clk->ustate_ac = -1; 567f3867f43SBen Skeggs clk->ustate_dc = -1; 5686625f55cSBen Skeggs clk->allow_reclock = allow_reclock; 569f3867f43SBen Skeggs 5707632b30eSBen Skeggs INIT_WORK(&clk->work, nvkm_pstate_work); 571f3867f43SBen Skeggs init_waitqueue_head(&clk->wait); 572f3867f43SBen Skeggs atomic_set(&clk->waiting, 0); 573f3867f43SBen Skeggs 574f3867f43SBen Skeggs /* If no pstates are provided, try and fetch them from the BIOS */ 5756625f55cSBen Skeggs if (!func->pstates) { 576f3867f43SBen Skeggs idx = 0; 577f3867f43SBen Skeggs do { 5787632b30eSBen Skeggs ret = nvkm_pstate_new(clk, idx++); 579f3867f43SBen Skeggs } while (ret == 0); 580f3867f43SBen Skeggs } else { 5816625f55cSBen Skeggs for (idx = 0; idx < func->nr_pstates; idx++) 5826625f55cSBen Skeggs list_add_tail(&func->pstates[idx].head, &clk->states); 5836625f55cSBen Skeggs clk->state_nr = func->nr_pstates; 584f3867f43SBen Skeggs } 585f3867f43SBen Skeggs 5867632b30eSBen Skeggs ret = nvkm_notify_init(NULL, &device->event, nvkm_clk_pwrsrc, true, 587f3867f43SBen Skeggs NULL, 0, 0, &clk->pwrsrc_ntfy); 588f3867f43SBen Skeggs if (ret) 589f3867f43SBen Skeggs return ret; 590f3867f43SBen Skeggs 5917632b30eSBen Skeggs mode = nvkm_stropt(device->cfgopt, "NvClkMode", &arglen); 592f3867f43SBen Skeggs if (mode) { 5937632b30eSBen Skeggs clk->ustate_ac = nvkm_clk_nstate(clk, mode, arglen); 5947632b30eSBen Skeggs clk->ustate_dc = nvkm_clk_nstate(clk, mode, arglen); 595f3867f43SBen Skeggs } 596f3867f43SBen Skeggs 5977632b30eSBen Skeggs mode = nvkm_stropt(device->cfgopt, "NvClkModeAC", &arglen); 598f3867f43SBen Skeggs if (mode) 5997632b30eSBen Skeggs clk->ustate_ac = nvkm_clk_nstate(clk, mode, arglen); 600f3867f43SBen Skeggs 6017632b30eSBen Skeggs mode = nvkm_stropt(device->cfgopt, "NvClkModeDC", &arglen); 602f3867f43SBen Skeggs if (mode) 6037632b30eSBen Skeggs clk->ustate_dc = nvkm_clk_nstate(clk, mode, arglen); 604f3867f43SBen Skeggs 605f3867f43SBen Skeggs return 0; 606f3867f43SBen Skeggs } 6076625f55cSBen Skeggs 6086625f55cSBen Skeggs int 6096625f55cSBen Skeggs nvkm_clk_new_(const struct nvkm_clk_func *func, struct nvkm_device *device, 6106625f55cSBen Skeggs int index, bool allow_reclock, struct nvkm_clk **pclk) 6116625f55cSBen Skeggs { 6126625f55cSBen Skeggs if (!(*pclk = kzalloc(sizeof(**pclk), GFP_KERNEL))) 6136625f55cSBen Skeggs return -ENOMEM; 6146625f55cSBen Skeggs return nvkm_clk_ctor(func, device, index, allow_reclock, *pclk); 6156625f55cSBen Skeggs } 616