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 */ 24f3867f43SBen Skeggs #include <subdev/clk.h> 25f3867f43SBen Skeggs #include <subdev/bios.h> 26f3867f43SBen Skeggs #include <subdev/bios/boost.h> 27f3867f43SBen Skeggs #include <subdev/bios/cstep.h> 28f3867f43SBen Skeggs #include <subdev/bios/perf.h> 297632b30eSBen Skeggs #include <subdev/fb.h> 307632b30eSBen Skeggs #include <subdev/therm.h> 317632b30eSBen Skeggs #include <subdev/volt.h> 327632b30eSBen Skeggs 337632b30eSBen Skeggs #include <core/option.h> 34f3867f43SBen Skeggs 35f3867f43SBen Skeggs /****************************************************************************** 36f3867f43SBen Skeggs * misc 37f3867f43SBen Skeggs *****************************************************************************/ 38f3867f43SBen Skeggs static u32 397632b30eSBen Skeggs nvkm_clk_adjust(struct nvkm_clk *clk, bool adjust, 40f3867f43SBen Skeggs u8 pstate, u8 domain, u32 input) 41f3867f43SBen Skeggs { 4246484438SBen Skeggs struct nvkm_bios *bios = clk->subdev.device->bios; 43f3867f43SBen Skeggs struct nvbios_boostE boostE; 44f3867f43SBen Skeggs u8 ver, hdr, cnt, len; 45f3867f43SBen Skeggs u16 data; 46f3867f43SBen Skeggs 47f3867f43SBen Skeggs data = nvbios_boostEm(bios, pstate, &ver, &hdr, &cnt, &len, &boostE); 48f3867f43SBen Skeggs if (data) { 49f3867f43SBen Skeggs struct nvbios_boostS boostS; 50f3867f43SBen Skeggs u8 idx = 0, sver, shdr; 51f3867f43SBen Skeggs u16 subd; 52f3867f43SBen Skeggs 53f3867f43SBen Skeggs input = max(boostE.min, input); 54f3867f43SBen Skeggs input = min(boostE.max, input); 55f3867f43SBen Skeggs do { 56f3867f43SBen Skeggs sver = ver; 57f3867f43SBen Skeggs shdr = hdr; 58f3867f43SBen Skeggs subd = nvbios_boostSp(bios, idx++, data, &sver, &shdr, 59f3867f43SBen Skeggs cnt, len, &boostS); 60f3867f43SBen Skeggs if (subd && boostS.domain == domain) { 61f3867f43SBen Skeggs if (adjust) 62f3867f43SBen Skeggs input = input * boostS.percent / 100; 63f3867f43SBen Skeggs input = max(boostS.min, input); 64f3867f43SBen Skeggs input = min(boostS.max, input); 65f3867f43SBen Skeggs break; 66f3867f43SBen Skeggs } 67f3867f43SBen Skeggs } while (subd); 68f3867f43SBen Skeggs } 69f3867f43SBen Skeggs 70f3867f43SBen Skeggs return input; 71f3867f43SBen Skeggs } 72f3867f43SBen Skeggs 73f3867f43SBen Skeggs /****************************************************************************** 74f3867f43SBen Skeggs * C-States 75f3867f43SBen Skeggs *****************************************************************************/ 76f3867f43SBen Skeggs static int 777632b30eSBen Skeggs nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) 78f3867f43SBen Skeggs { 79b907649eSBen Skeggs struct nvkm_subdev *subdev = &clk->subdev; 80b907649eSBen Skeggs struct nvkm_device *device = subdev->device; 81b907649eSBen Skeggs struct nvkm_therm *therm = device->therm; 82b907649eSBen Skeggs struct nvkm_volt *volt = device->volt; 837632b30eSBen Skeggs struct nvkm_cstate *cstate; 84f3867f43SBen Skeggs int ret; 85f3867f43SBen Skeggs 86f3867f43SBen Skeggs if (!list_empty(&pstate->list)) { 87f3867f43SBen Skeggs cstate = list_entry(pstate->list.prev, typeof(*cstate), head); 88f3867f43SBen Skeggs } else { 89f3867f43SBen Skeggs cstate = &pstate->base; 90f3867f43SBen Skeggs } 91f3867f43SBen Skeggs 92da06b46bSBen Skeggs if (therm) { 93da06b46bSBen Skeggs ret = nvkm_therm_cstate(therm, pstate->fanspeed, +1); 94f3867f43SBen Skeggs if (ret && ret != -ENODEV) { 95b907649eSBen Skeggs nvkm_error(subdev, "failed to raise fan speed: %d\n", ret); 96f3867f43SBen Skeggs return ret; 97f3867f43SBen Skeggs } 98f3867f43SBen Skeggs } 99f3867f43SBen Skeggs 100f3867f43SBen Skeggs if (volt) { 101f3867f43SBen Skeggs ret = volt->set_id(volt, cstate->voltage, +1); 102f3867f43SBen Skeggs if (ret && ret != -ENODEV) { 103b907649eSBen Skeggs nvkm_error(subdev, "failed to raise voltage: %d\n", ret); 104f3867f43SBen Skeggs return ret; 105f3867f43SBen Skeggs } 106f3867f43SBen Skeggs } 107f3867f43SBen Skeggs 108f3867f43SBen Skeggs ret = clk->calc(clk, cstate); 109f3867f43SBen Skeggs if (ret == 0) { 110f3867f43SBen Skeggs ret = clk->prog(clk); 111f3867f43SBen Skeggs clk->tidy(clk); 112f3867f43SBen Skeggs } 113f3867f43SBen Skeggs 114f3867f43SBen Skeggs if (volt) { 115f3867f43SBen Skeggs ret = volt->set_id(volt, cstate->voltage, -1); 116f3867f43SBen Skeggs if (ret && ret != -ENODEV) 117b907649eSBen Skeggs nvkm_error(subdev, "failed to lower voltage: %d\n", ret); 118f3867f43SBen Skeggs } 119f3867f43SBen Skeggs 120da06b46bSBen Skeggs if (therm) { 121da06b46bSBen Skeggs ret = nvkm_therm_cstate(therm, pstate->fanspeed, -1); 122f3867f43SBen Skeggs if (ret && ret != -ENODEV) 123b907649eSBen Skeggs nvkm_error(subdev, "failed to lower fan speed: %d\n", ret); 124f3867f43SBen Skeggs } 125f3867f43SBen Skeggs 1263eca809bSBen Skeggs return ret; 127f3867f43SBen Skeggs } 128f3867f43SBen Skeggs 129f3867f43SBen Skeggs static void 1307632b30eSBen Skeggs nvkm_cstate_del(struct nvkm_cstate *cstate) 131f3867f43SBen Skeggs { 132f3867f43SBen Skeggs list_del(&cstate->head); 133f3867f43SBen Skeggs kfree(cstate); 134f3867f43SBen Skeggs } 135f3867f43SBen Skeggs 136f3867f43SBen Skeggs static int 1377632b30eSBen Skeggs nvkm_cstate_new(struct nvkm_clk *clk, int idx, struct nvkm_pstate *pstate) 138f3867f43SBen Skeggs { 13946484438SBen Skeggs struct nvkm_bios *bios = clk->subdev.device->bios; 1407632b30eSBen Skeggs struct nvkm_domain *domain = clk->domains; 1417632b30eSBen Skeggs struct nvkm_cstate *cstate = NULL; 142f3867f43SBen Skeggs struct nvbios_cstepX cstepX; 143f3867f43SBen Skeggs u8 ver, hdr; 144f3867f43SBen Skeggs u16 data; 145f3867f43SBen Skeggs 146f3867f43SBen Skeggs data = nvbios_cstepXp(bios, idx, &ver, &hdr, &cstepX); 147f3867f43SBen Skeggs if (!data) 148f3867f43SBen Skeggs return -ENOENT; 149f3867f43SBen Skeggs 150f3867f43SBen Skeggs cstate = kzalloc(sizeof(*cstate), GFP_KERNEL); 151f3867f43SBen Skeggs if (!cstate) 152f3867f43SBen Skeggs return -ENOMEM; 153f3867f43SBen Skeggs 154f3867f43SBen Skeggs *cstate = pstate->base; 155f3867f43SBen Skeggs cstate->voltage = cstepX.voltage; 156f3867f43SBen Skeggs 157f3867f43SBen Skeggs while (domain && domain->name != nv_clk_src_max) { 158f3867f43SBen Skeggs if (domain->flags & NVKM_CLK_DOM_FLAG_CORE) { 1597632b30eSBen Skeggs u32 freq = nvkm_clk_adjust(clk, true, pstate->pstate, 1607632b30eSBen Skeggs domain->bios, cstepX.freq); 161f3867f43SBen Skeggs cstate->domain[domain->name] = freq; 162f3867f43SBen Skeggs } 163f3867f43SBen Skeggs domain++; 164f3867f43SBen Skeggs } 165f3867f43SBen Skeggs 166f3867f43SBen Skeggs list_add(&cstate->head, &pstate->list); 167f3867f43SBen Skeggs return 0; 168f3867f43SBen Skeggs } 169f3867f43SBen Skeggs 170f3867f43SBen Skeggs /****************************************************************************** 171f3867f43SBen Skeggs * P-States 172f3867f43SBen Skeggs *****************************************************************************/ 173f3867f43SBen Skeggs static int 1747632b30eSBen Skeggs nvkm_pstate_prog(struct nvkm_clk *clk, int pstatei) 175f3867f43SBen Skeggs { 176b907649eSBen Skeggs struct nvkm_subdev *subdev = &clk->subdev; 177d36a99d2SBen Skeggs struct nvkm_ram *ram = subdev->device->fb->ram; 1787632b30eSBen Skeggs struct nvkm_pstate *pstate; 179f3867f43SBen Skeggs int ret, idx = 0; 180f3867f43SBen Skeggs 181f3867f43SBen Skeggs list_for_each_entry(pstate, &clk->states, head) { 182f3867f43SBen Skeggs if (idx++ == pstatei) 183f3867f43SBen Skeggs break; 184f3867f43SBen Skeggs } 185f3867f43SBen Skeggs 186b907649eSBen Skeggs nvkm_debug(subdev, "setting performance state %d\n", pstatei); 187f3867f43SBen Skeggs clk->pstate = pstatei; 188f3867f43SBen Skeggs 189d36a99d2SBen Skeggs if (ram && ram->func->calc) { 190f3867f43SBen Skeggs int khz = pstate->base.domain[nv_clk_src_mem]; 191f3867f43SBen Skeggs do { 192d36a99d2SBen Skeggs ret = ram->func->calc(ram, khz); 193f3867f43SBen Skeggs if (ret == 0) 194d36a99d2SBen Skeggs ret = ram->func->prog(ram); 195f3867f43SBen Skeggs } while (ret > 0); 196d36a99d2SBen Skeggs ram->func->tidy(ram); 197f3867f43SBen Skeggs } 198f3867f43SBen Skeggs 1997632b30eSBen Skeggs return nvkm_cstate_prog(clk, pstate, 0); 200f3867f43SBen Skeggs } 201f3867f43SBen Skeggs 202f3867f43SBen Skeggs static void 2037632b30eSBen Skeggs nvkm_pstate_work(struct work_struct *work) 204f3867f43SBen Skeggs { 2057632b30eSBen Skeggs struct nvkm_clk *clk = container_of(work, typeof(*clk), work); 206b907649eSBen Skeggs struct nvkm_subdev *subdev = &clk->subdev; 207f3867f43SBen Skeggs int pstate; 208f3867f43SBen Skeggs 209f3867f43SBen Skeggs if (!atomic_xchg(&clk->waiting, 0)) 210f3867f43SBen Skeggs return; 211f3867f43SBen Skeggs clk->pwrsrc = power_supply_is_system_supplied(); 212f3867f43SBen Skeggs 213b907649eSBen Skeggs nvkm_trace(subdev, "P %d PWR %d U(AC) %d U(DC) %d A %d T %d D %d\n", 214f3867f43SBen Skeggs clk->pstate, clk->pwrsrc, clk->ustate_ac, clk->ustate_dc, 215f3867f43SBen Skeggs clk->astate, clk->tstate, clk->dstate); 216f3867f43SBen Skeggs 217f3867f43SBen Skeggs pstate = clk->pwrsrc ? clk->ustate_ac : clk->ustate_dc; 218f3867f43SBen Skeggs if (clk->state_nr && pstate != -1) { 219f3867f43SBen Skeggs pstate = (pstate < 0) ? clk->astate : pstate; 22085fa319dSWei Ni pstate = min(pstate, clk->state_nr - 1 + clk->tstate); 221f3867f43SBen Skeggs pstate = max(pstate, clk->dstate); 222f3867f43SBen Skeggs } else { 223f3867f43SBen Skeggs pstate = clk->pstate = -1; 224f3867f43SBen Skeggs } 225f3867f43SBen Skeggs 226b907649eSBen Skeggs nvkm_trace(subdev, "-> %d\n", pstate); 227f3867f43SBen Skeggs if (pstate != clk->pstate) { 2287632b30eSBen Skeggs int ret = nvkm_pstate_prog(clk, pstate); 229f3867f43SBen Skeggs if (ret) { 230b907649eSBen Skeggs nvkm_error(subdev, "error setting pstate %d: %d\n", 231f3867f43SBen Skeggs pstate, ret); 232f3867f43SBen Skeggs } 233f3867f43SBen Skeggs } 234f3867f43SBen Skeggs 235f3867f43SBen Skeggs wake_up_all(&clk->wait); 236f3867f43SBen Skeggs nvkm_notify_get(&clk->pwrsrc_ntfy); 237f3867f43SBen Skeggs } 238f3867f43SBen Skeggs 239f3867f43SBen Skeggs static int 2407632b30eSBen Skeggs nvkm_pstate_calc(struct nvkm_clk *clk, bool wait) 241f3867f43SBen Skeggs { 242f3867f43SBen Skeggs atomic_set(&clk->waiting, 1); 243f3867f43SBen Skeggs schedule_work(&clk->work); 244f3867f43SBen Skeggs if (wait) 245f3867f43SBen Skeggs wait_event(clk->wait, !atomic_read(&clk->waiting)); 246f3867f43SBen Skeggs return 0; 247f3867f43SBen Skeggs } 248f3867f43SBen Skeggs 249f3867f43SBen Skeggs static void 2507632b30eSBen Skeggs nvkm_pstate_info(struct nvkm_clk *clk, struct nvkm_pstate *pstate) 251f3867f43SBen Skeggs { 2527632b30eSBen Skeggs struct nvkm_domain *clock = clk->domains - 1; 2537632b30eSBen Skeggs struct nvkm_cstate *cstate; 254b907649eSBen Skeggs struct nvkm_subdev *subdev = &clk->subdev; 255f3867f43SBen Skeggs char info[3][32] = { "", "", "" }; 256f3867f43SBen Skeggs char name[4] = "--"; 257f3867f43SBen Skeggs int i = -1; 258f3867f43SBen Skeggs 259f3867f43SBen Skeggs if (pstate->pstate != 0xff) 260f3867f43SBen Skeggs snprintf(name, sizeof(name), "%02x", pstate->pstate); 261f3867f43SBen Skeggs 262f3867f43SBen Skeggs while ((++clock)->name != nv_clk_src_max) { 263f3867f43SBen Skeggs u32 lo = pstate->base.domain[clock->name]; 264f3867f43SBen Skeggs u32 hi = lo; 265f3867f43SBen Skeggs if (hi == 0) 266f3867f43SBen Skeggs continue; 267f3867f43SBen Skeggs 268b907649eSBen Skeggs nvkm_debug(subdev, "%02x: %10d KHz\n", clock->name, lo); 269f3867f43SBen Skeggs list_for_each_entry(cstate, &pstate->list, head) { 270f3867f43SBen Skeggs u32 freq = cstate->domain[clock->name]; 271f3867f43SBen Skeggs lo = min(lo, freq); 272f3867f43SBen Skeggs hi = max(hi, freq); 273b907649eSBen Skeggs nvkm_debug(subdev, "%10d KHz\n", freq); 274f3867f43SBen Skeggs } 275f3867f43SBen Skeggs 276f3867f43SBen Skeggs if (clock->mname && ++i < ARRAY_SIZE(info)) { 277f3867f43SBen Skeggs lo /= clock->mdiv; 278f3867f43SBen Skeggs hi /= clock->mdiv; 279f3867f43SBen Skeggs if (lo == hi) { 280f3867f43SBen Skeggs snprintf(info[i], sizeof(info[i]), "%s %d MHz", 281f3867f43SBen Skeggs clock->mname, lo); 282f3867f43SBen Skeggs } else { 283f3867f43SBen Skeggs snprintf(info[i], sizeof(info[i]), 284f3867f43SBen Skeggs "%s %d-%d MHz", clock->mname, lo, hi); 285f3867f43SBen Skeggs } 286f3867f43SBen Skeggs } 287f3867f43SBen Skeggs } 288f3867f43SBen Skeggs 289b907649eSBen Skeggs nvkm_debug(subdev, "%s: %s %s %s\n", name, info[0], info[1], info[2]); 290f3867f43SBen Skeggs } 291f3867f43SBen Skeggs 292f3867f43SBen Skeggs static void 2937632b30eSBen Skeggs nvkm_pstate_del(struct nvkm_pstate *pstate) 294f3867f43SBen Skeggs { 2957632b30eSBen Skeggs struct nvkm_cstate *cstate, *temp; 296f3867f43SBen Skeggs 297f3867f43SBen Skeggs list_for_each_entry_safe(cstate, temp, &pstate->list, head) { 2987632b30eSBen Skeggs nvkm_cstate_del(cstate); 299f3867f43SBen Skeggs } 300f3867f43SBen Skeggs 301f3867f43SBen Skeggs list_del(&pstate->head); 302f3867f43SBen Skeggs kfree(pstate); 303f3867f43SBen Skeggs } 304f3867f43SBen Skeggs 305f3867f43SBen Skeggs static int 3067632b30eSBen Skeggs nvkm_pstate_new(struct nvkm_clk *clk, int idx) 307f3867f43SBen Skeggs { 30846484438SBen Skeggs struct nvkm_bios *bios = clk->subdev.device->bios; 3097632b30eSBen Skeggs struct nvkm_domain *domain = clk->domains - 1; 3107632b30eSBen Skeggs struct nvkm_pstate *pstate; 3117632b30eSBen Skeggs struct nvkm_cstate *cstate; 312f3867f43SBen Skeggs struct nvbios_cstepE cstepE; 313f3867f43SBen Skeggs struct nvbios_perfE perfE; 314f3867f43SBen Skeggs u8 ver, hdr, cnt, len; 315f3867f43SBen Skeggs u16 data; 316f3867f43SBen Skeggs 317f3867f43SBen Skeggs data = nvbios_perfEp(bios, idx, &ver, &hdr, &cnt, &len, &perfE); 318f3867f43SBen Skeggs if (!data) 319f3867f43SBen Skeggs return -EINVAL; 320f3867f43SBen Skeggs if (perfE.pstate == 0xff) 321f3867f43SBen Skeggs return 0; 322f3867f43SBen Skeggs 323f3867f43SBen Skeggs pstate = kzalloc(sizeof(*pstate), GFP_KERNEL); 324f3867f43SBen Skeggs cstate = &pstate->base; 325f3867f43SBen Skeggs if (!pstate) 326f3867f43SBen Skeggs return -ENOMEM; 327f3867f43SBen Skeggs 328f3867f43SBen Skeggs INIT_LIST_HEAD(&pstate->list); 329f3867f43SBen Skeggs 330f3867f43SBen Skeggs pstate->pstate = perfE.pstate; 331f3867f43SBen Skeggs pstate->fanspeed = perfE.fanspeed; 332f3867f43SBen Skeggs cstate->voltage = perfE.voltage; 333f3867f43SBen Skeggs cstate->domain[nv_clk_src_core] = perfE.core; 334f3867f43SBen Skeggs cstate->domain[nv_clk_src_shader] = perfE.shader; 335f3867f43SBen Skeggs cstate->domain[nv_clk_src_mem] = perfE.memory; 336f3867f43SBen Skeggs cstate->domain[nv_clk_src_vdec] = perfE.vdec; 337f3867f43SBen Skeggs cstate->domain[nv_clk_src_dom6] = perfE.disp; 338f3867f43SBen Skeggs 339f3867f43SBen Skeggs while (ver >= 0x40 && (++domain)->name != nv_clk_src_max) { 340f3867f43SBen Skeggs struct nvbios_perfS perfS; 341f3867f43SBen Skeggs u8 sver = ver, shdr = hdr; 342f3867f43SBen Skeggs u32 perfSe = nvbios_perfSp(bios, data, domain->bios, 343f3867f43SBen Skeggs &sver, &shdr, cnt, len, &perfS); 344f3867f43SBen Skeggs if (perfSe == 0 || sver != 0x40) 345f3867f43SBen Skeggs continue; 346f3867f43SBen Skeggs 347f3867f43SBen Skeggs if (domain->flags & NVKM_CLK_DOM_FLAG_CORE) { 3487632b30eSBen Skeggs perfS.v40.freq = nvkm_clk_adjust(clk, false, 349f3867f43SBen Skeggs pstate->pstate, 350f3867f43SBen Skeggs domain->bios, 351f3867f43SBen Skeggs perfS.v40.freq); 352f3867f43SBen Skeggs } 353f3867f43SBen Skeggs 354f3867f43SBen Skeggs cstate->domain[domain->name] = perfS.v40.freq; 355f3867f43SBen Skeggs } 356f3867f43SBen Skeggs 357f3867f43SBen Skeggs data = nvbios_cstepEm(bios, pstate->pstate, &ver, &hdr, &cstepE); 358f3867f43SBen Skeggs if (data) { 359f3867f43SBen Skeggs int idx = cstepE.index; 360f3867f43SBen Skeggs do { 3617632b30eSBen Skeggs nvkm_cstate_new(clk, idx, pstate); 362f3867f43SBen Skeggs } while(idx--); 363f3867f43SBen Skeggs } 364f3867f43SBen Skeggs 3657632b30eSBen Skeggs nvkm_pstate_info(clk, pstate); 366f3867f43SBen Skeggs list_add_tail(&pstate->head, &clk->states); 367f3867f43SBen Skeggs clk->state_nr++; 368f3867f43SBen Skeggs return 0; 369f3867f43SBen Skeggs } 370f3867f43SBen Skeggs 371f3867f43SBen Skeggs /****************************************************************************** 372f3867f43SBen Skeggs * Adjustment triggers 373f3867f43SBen Skeggs *****************************************************************************/ 374f3867f43SBen Skeggs static int 3757632b30eSBen Skeggs nvkm_clk_ustate_update(struct nvkm_clk *clk, int req) 376f3867f43SBen Skeggs { 3777632b30eSBen Skeggs struct nvkm_pstate *pstate; 378f3867f43SBen Skeggs int i = 0; 379f3867f43SBen Skeggs 380f3867f43SBen Skeggs if (!clk->allow_reclock) 381f3867f43SBen Skeggs return -ENOSYS; 382f3867f43SBen Skeggs 383f3867f43SBen Skeggs if (req != -1 && req != -2) { 384f3867f43SBen Skeggs list_for_each_entry(pstate, &clk->states, head) { 385f3867f43SBen Skeggs if (pstate->pstate == req) 386f3867f43SBen Skeggs break; 387f3867f43SBen Skeggs i++; 388f3867f43SBen Skeggs } 389f3867f43SBen Skeggs 390f3867f43SBen Skeggs if (pstate->pstate != req) 391f3867f43SBen Skeggs return -EINVAL; 392f3867f43SBen Skeggs req = i; 393f3867f43SBen Skeggs } 394f3867f43SBen Skeggs 395f3867f43SBen Skeggs return req + 2; 396f3867f43SBen Skeggs } 397f3867f43SBen Skeggs 398f3867f43SBen Skeggs static int 3997632b30eSBen Skeggs nvkm_clk_nstate(struct nvkm_clk *clk, const char *mode, int arglen) 400f3867f43SBen Skeggs { 401f3867f43SBen Skeggs int ret = 1; 402f3867f43SBen Skeggs 403f3867f43SBen Skeggs if (clk->allow_reclock && !strncasecmpz(mode, "auto", arglen)) 404f3867f43SBen Skeggs return -2; 405f3867f43SBen Skeggs 406f3867f43SBen Skeggs if (strncasecmpz(mode, "disabled", arglen)) { 407f3867f43SBen Skeggs char save = mode[arglen]; 408f3867f43SBen Skeggs long v; 409f3867f43SBen Skeggs 410f3867f43SBen Skeggs ((char *)mode)[arglen] = '\0'; 411f3867f43SBen Skeggs if (!kstrtol(mode, 0, &v)) { 4127632b30eSBen Skeggs ret = nvkm_clk_ustate_update(clk, v); 413f3867f43SBen Skeggs if (ret < 0) 414f3867f43SBen Skeggs ret = 1; 415f3867f43SBen Skeggs } 416f3867f43SBen Skeggs ((char *)mode)[arglen] = save; 417f3867f43SBen Skeggs } 418f3867f43SBen Skeggs 419f3867f43SBen Skeggs return ret - 2; 420f3867f43SBen Skeggs } 421f3867f43SBen Skeggs 422f3867f43SBen Skeggs int 4237632b30eSBen Skeggs nvkm_clk_ustate(struct nvkm_clk *clk, int req, int pwr) 424f3867f43SBen Skeggs { 4257632b30eSBen Skeggs int ret = nvkm_clk_ustate_update(clk, req); 426f3867f43SBen Skeggs if (ret >= 0) { 427f3867f43SBen Skeggs if (ret -= 2, pwr) clk->ustate_ac = ret; 428f3867f43SBen Skeggs else clk->ustate_dc = ret; 4297632b30eSBen Skeggs return nvkm_pstate_calc(clk, true); 430f3867f43SBen Skeggs } 431f3867f43SBen Skeggs return ret; 432f3867f43SBen Skeggs } 433f3867f43SBen Skeggs 434f3867f43SBen Skeggs int 4357632b30eSBen Skeggs nvkm_clk_astate(struct nvkm_clk *clk, int req, int rel, bool wait) 436f3867f43SBen Skeggs { 437f3867f43SBen Skeggs if (!rel) clk->astate = req; 438f3867f43SBen Skeggs if ( rel) clk->astate += rel; 439f3867f43SBen Skeggs clk->astate = min(clk->astate, clk->state_nr - 1); 440f3867f43SBen Skeggs clk->astate = max(clk->astate, 0); 4417632b30eSBen Skeggs return nvkm_pstate_calc(clk, wait); 442f3867f43SBen Skeggs } 443f3867f43SBen Skeggs 444f3867f43SBen Skeggs int 4457632b30eSBen Skeggs nvkm_clk_tstate(struct nvkm_clk *clk, int req, int rel) 446f3867f43SBen Skeggs { 447f3867f43SBen Skeggs if (!rel) clk->tstate = req; 448f3867f43SBen Skeggs if ( rel) clk->tstate += rel; 449f3867f43SBen Skeggs clk->tstate = min(clk->tstate, 0); 450f3867f43SBen Skeggs clk->tstate = max(clk->tstate, -(clk->state_nr - 1)); 4517632b30eSBen Skeggs return nvkm_pstate_calc(clk, true); 452f3867f43SBen Skeggs } 453f3867f43SBen Skeggs 454f3867f43SBen Skeggs int 4557632b30eSBen Skeggs nvkm_clk_dstate(struct nvkm_clk *clk, int req, int rel) 456f3867f43SBen Skeggs { 457f3867f43SBen Skeggs if (!rel) clk->dstate = req; 458f3867f43SBen Skeggs if ( rel) clk->dstate += rel; 459f3867f43SBen Skeggs clk->dstate = min(clk->dstate, clk->state_nr - 1); 460f3867f43SBen Skeggs clk->dstate = max(clk->dstate, 0); 4617632b30eSBen Skeggs return nvkm_pstate_calc(clk, true); 462f3867f43SBen Skeggs } 463f3867f43SBen Skeggs 464f3867f43SBen Skeggs static int 4657632b30eSBen Skeggs nvkm_clk_pwrsrc(struct nvkm_notify *notify) 466f3867f43SBen Skeggs { 4677632b30eSBen Skeggs struct nvkm_clk *clk = 468f3867f43SBen Skeggs container_of(notify, typeof(*clk), pwrsrc_ntfy); 4697632b30eSBen Skeggs nvkm_pstate_calc(clk, false); 470f3867f43SBen Skeggs return NVKM_NOTIFY_DROP; 471f3867f43SBen Skeggs } 472f3867f43SBen Skeggs 473f3867f43SBen Skeggs /****************************************************************************** 474f3867f43SBen Skeggs * subdev base class implementation 475f3867f43SBen Skeggs *****************************************************************************/ 476f3867f43SBen Skeggs 477f3867f43SBen Skeggs int 4787632b30eSBen Skeggs _nvkm_clk_fini(struct nvkm_object *object, bool suspend) 479f3867f43SBen Skeggs { 4807632b30eSBen Skeggs struct nvkm_clk *clk = (void *)object; 481f3867f43SBen Skeggs nvkm_notify_put(&clk->pwrsrc_ntfy); 4823a8c3400SBen Skeggs return nvkm_subdev_fini_old(&clk->subdev, suspend); 483f3867f43SBen Skeggs } 484f3867f43SBen Skeggs 485f3867f43SBen Skeggs int 4867632b30eSBen Skeggs _nvkm_clk_init(struct nvkm_object *object) 487f3867f43SBen Skeggs { 4887632b30eSBen Skeggs struct nvkm_clk *clk = (void *)object; 489b907649eSBen Skeggs struct nvkm_subdev *subdev = &clk->subdev; 4907632b30eSBen Skeggs struct nvkm_domain *clock = clk->domains; 491f3867f43SBen Skeggs int ret; 492f3867f43SBen Skeggs 4933a8c3400SBen Skeggs ret = nvkm_subdev_init_old(&clk->subdev); 494f3867f43SBen Skeggs if (ret) 495f3867f43SBen Skeggs return ret; 496f3867f43SBen Skeggs 497f3867f43SBen Skeggs memset(&clk->bstate, 0x00, sizeof(clk->bstate)); 498f3867f43SBen Skeggs INIT_LIST_HEAD(&clk->bstate.list); 499f3867f43SBen Skeggs clk->bstate.pstate = 0xff; 500f3867f43SBen Skeggs 501f3867f43SBen Skeggs while (clock->name != nv_clk_src_max) { 502f3867f43SBen Skeggs ret = clk->read(clk, clock->name); 503f3867f43SBen Skeggs if (ret < 0) { 504b907649eSBen Skeggs nvkm_error(subdev, "%02x freq unknown\n", clock->name); 505f3867f43SBen Skeggs return ret; 506f3867f43SBen Skeggs } 507f3867f43SBen Skeggs clk->bstate.base.domain[clock->name] = ret; 508f3867f43SBen Skeggs clock++; 509f3867f43SBen Skeggs } 510f3867f43SBen Skeggs 5117632b30eSBen Skeggs nvkm_pstate_info(clk, &clk->bstate); 512f3867f43SBen Skeggs 513f3867f43SBen Skeggs clk->astate = clk->state_nr - 1; 514f3867f43SBen Skeggs clk->tstate = 0; 515f3867f43SBen Skeggs clk->dstate = 0; 516f3867f43SBen Skeggs clk->pstate = -1; 5177632b30eSBen Skeggs nvkm_pstate_calc(clk, true); 518f3867f43SBen Skeggs return 0; 519f3867f43SBen Skeggs } 520f3867f43SBen Skeggs 521f3867f43SBen Skeggs void 5227632b30eSBen Skeggs _nvkm_clk_dtor(struct nvkm_object *object) 523f3867f43SBen Skeggs { 5247632b30eSBen Skeggs struct nvkm_clk *clk = (void *)object; 5257632b30eSBen Skeggs struct nvkm_pstate *pstate, *temp; 526f3867f43SBen Skeggs 527f3867f43SBen Skeggs nvkm_notify_fini(&clk->pwrsrc_ntfy); 528f3867f43SBen Skeggs 529f3867f43SBen Skeggs list_for_each_entry_safe(pstate, temp, &clk->states, head) { 5307632b30eSBen Skeggs nvkm_pstate_del(pstate); 531f3867f43SBen Skeggs } 532f3867f43SBen Skeggs 5333eca809bSBen Skeggs nvkm_subdev_destroy(&clk->subdev); 534f3867f43SBen Skeggs } 535f3867f43SBen Skeggs 536f3867f43SBen Skeggs int 5377632b30eSBen Skeggs nvkm_clk_create_(struct nvkm_object *parent, struct nvkm_object *engine, 5387632b30eSBen Skeggs struct nvkm_oclass *oclass, struct nvkm_domain *clocks, 5397632b30eSBen Skeggs struct nvkm_pstate *pstates, int nb_pstates, 5407632b30eSBen Skeggs bool allow_reclock, int length, void **object) 541f3867f43SBen Skeggs { 5427632b30eSBen Skeggs struct nvkm_device *device = nv_device(parent); 5437632b30eSBen Skeggs struct nvkm_clk *clk; 544f3867f43SBen Skeggs int ret, idx, arglen; 545f3867f43SBen Skeggs const char *mode; 546f3867f43SBen Skeggs 5477632b30eSBen Skeggs ret = nvkm_subdev_create_(parent, engine, oclass, 0, "CLK", 548f3867f43SBen Skeggs "clock", length, object); 549f3867f43SBen Skeggs clk = *object; 550f3867f43SBen Skeggs if (ret) 551f3867f43SBen Skeggs return ret; 552f3867f43SBen Skeggs 553f3867f43SBen Skeggs INIT_LIST_HEAD(&clk->states); 554f3867f43SBen Skeggs clk->domains = clocks; 555f3867f43SBen Skeggs clk->ustate_ac = -1; 556f3867f43SBen Skeggs clk->ustate_dc = -1; 557f3867f43SBen Skeggs 5587632b30eSBen Skeggs INIT_WORK(&clk->work, nvkm_pstate_work); 559f3867f43SBen Skeggs init_waitqueue_head(&clk->wait); 560f3867f43SBen Skeggs atomic_set(&clk->waiting, 0); 561f3867f43SBen Skeggs 562f3867f43SBen Skeggs /* If no pstates are provided, try and fetch them from the BIOS */ 563f3867f43SBen Skeggs if (!pstates) { 564f3867f43SBen Skeggs idx = 0; 565f3867f43SBen Skeggs do { 5667632b30eSBen Skeggs ret = nvkm_pstate_new(clk, idx++); 567f3867f43SBen Skeggs } while (ret == 0); 568f3867f43SBen Skeggs } else { 569f3867f43SBen Skeggs for (idx = 0; idx < nb_pstates; idx++) 570f3867f43SBen Skeggs list_add_tail(&pstates[idx].head, &clk->states); 571f3867f43SBen Skeggs clk->state_nr = nb_pstates; 572f3867f43SBen Skeggs } 573f3867f43SBen Skeggs 574f3867f43SBen Skeggs clk->allow_reclock = allow_reclock; 575f3867f43SBen Skeggs 5767632b30eSBen Skeggs ret = nvkm_notify_init(NULL, &device->event, nvkm_clk_pwrsrc, true, 577f3867f43SBen Skeggs NULL, 0, 0, &clk->pwrsrc_ntfy); 578f3867f43SBen Skeggs if (ret) 579f3867f43SBen Skeggs return ret; 580f3867f43SBen Skeggs 5817632b30eSBen Skeggs mode = nvkm_stropt(device->cfgopt, "NvClkMode", &arglen); 582f3867f43SBen Skeggs if (mode) { 5837632b30eSBen Skeggs clk->ustate_ac = nvkm_clk_nstate(clk, mode, arglen); 5847632b30eSBen Skeggs clk->ustate_dc = nvkm_clk_nstate(clk, mode, arglen); 585f3867f43SBen Skeggs } 586f3867f43SBen Skeggs 5877632b30eSBen Skeggs mode = nvkm_stropt(device->cfgopt, "NvClkModeAC", &arglen); 588f3867f43SBen Skeggs if (mode) 5897632b30eSBen Skeggs clk->ustate_ac = nvkm_clk_nstate(clk, mode, arglen); 590f3867f43SBen Skeggs 5917632b30eSBen Skeggs mode = nvkm_stropt(device->cfgopt, "NvClkModeDC", &arglen); 592f3867f43SBen Skeggs if (mode) 5937632b30eSBen Skeggs clk->ustate_dc = nvkm_clk_nstate(clk, mode, arglen); 594f3867f43SBen Skeggs 595f3867f43SBen Skeggs return 0; 596f3867f43SBen Skeggs } 597