xref: /openbmc/linux/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk104.c (revision d0034a7a4ac7fae708146ac0059b9c47a1543f0d)
17632b30eSBen Skeggs /*
27632b30eSBen Skeggs  * Copyright 2013 Red Hat Inc.
37632b30eSBen Skeggs  *
47632b30eSBen Skeggs  * Permission is hereby granted, free of charge, to any person obtaining a
57632b30eSBen Skeggs  * copy of this software and associated documentation files (the "Software"),
67632b30eSBen Skeggs  * to deal in the Software without restriction, including without limitation
77632b30eSBen Skeggs  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
87632b30eSBen Skeggs  * and/or sell copies of the Software, and to permit persons to whom the
97632b30eSBen Skeggs  * Software is furnished to do so, subject to the following conditions:
107632b30eSBen Skeggs  *
117632b30eSBen Skeggs  * The above copyright notice and this permission notice shall be included in
127632b30eSBen Skeggs  * all copies or substantial portions of the Software.
137632b30eSBen Skeggs  *
147632b30eSBen Skeggs  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
157632b30eSBen Skeggs  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
167632b30eSBen Skeggs  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
177632b30eSBen Skeggs  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
187632b30eSBen Skeggs  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
197632b30eSBen Skeggs  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
207632b30eSBen Skeggs  * OTHER DEALINGS IN THE SOFTWARE.
217632b30eSBen Skeggs  *
227632b30eSBen Skeggs  * Authors: Ben Skeggs
237632b30eSBen Skeggs  */
246625f55cSBen Skeggs #define gk104_clk(p) container_of((p), struct gk104_clk, base)
256625f55cSBen Skeggs #include "priv.h"
267632b30eSBen Skeggs #include "pll.h"
277632b30eSBen Skeggs 
287632b30eSBen Skeggs #include <subdev/timer.h>
297632b30eSBen Skeggs #include <subdev/bios.h>
307632b30eSBen Skeggs #include <subdev/bios/pll.h>
317632b30eSBen Skeggs 
327632b30eSBen Skeggs struct gk104_clk_info {
337632b30eSBen Skeggs 	u32 freq;
347632b30eSBen Skeggs 	u32 ssel;
357632b30eSBen Skeggs 	u32 mdiv;
367632b30eSBen Skeggs 	u32 dsrc;
377632b30eSBen Skeggs 	u32 ddiv;
387632b30eSBen Skeggs 	u32 coef;
397632b30eSBen Skeggs };
407632b30eSBen Skeggs 
413eca809bSBen Skeggs struct gk104_clk {
427632b30eSBen Skeggs 	struct nvkm_clk base;
437632b30eSBen Skeggs 	struct gk104_clk_info eng[16];
447632b30eSBen Skeggs };
457632b30eSBen Skeggs 
463eca809bSBen Skeggs static u32 read_div(struct gk104_clk *, int, u32, u32);
473eca809bSBen Skeggs static u32 read_pll(struct gk104_clk *, u32);
487632b30eSBen Skeggs 
497632b30eSBen Skeggs static u32
read_vco(struct gk104_clk * clk,u32 dsrc)503eca809bSBen Skeggs read_vco(struct gk104_clk *clk, u32 dsrc)
517632b30eSBen Skeggs {
52822ad79fSBen Skeggs 	struct nvkm_device *device = clk->base.subdev.device;
53822ad79fSBen Skeggs 	u32 ssrc = nvkm_rd32(device, dsrc);
547632b30eSBen Skeggs 	if (!(ssrc & 0x00000100))
553eca809bSBen Skeggs 		return read_pll(clk, 0x00e800);
563eca809bSBen Skeggs 	return read_pll(clk, 0x00e820);
577632b30eSBen Skeggs }
587632b30eSBen Skeggs 
597632b30eSBen Skeggs static u32
read_pll(struct gk104_clk * clk,u32 pll)603eca809bSBen Skeggs read_pll(struct gk104_clk *clk, u32 pll)
617632b30eSBen Skeggs {
62822ad79fSBen Skeggs 	struct nvkm_device *device = clk->base.subdev.device;
63822ad79fSBen Skeggs 	u32 ctrl = nvkm_rd32(device, pll + 0x00);
64822ad79fSBen Skeggs 	u32 coef = nvkm_rd32(device, pll + 0x04);
657632b30eSBen Skeggs 	u32 P = (coef & 0x003f0000) >> 16;
667632b30eSBen Skeggs 	u32 N = (coef & 0x0000ff00) >> 8;
677632b30eSBen Skeggs 	u32 M = (coef & 0x000000ff) >> 0;
687632b30eSBen Skeggs 	u32 sclk;
697632b30eSBen Skeggs 	u16 fN = 0xf000;
707632b30eSBen Skeggs 
717632b30eSBen Skeggs 	if (!(ctrl & 0x00000001))
727632b30eSBen Skeggs 		return 0;
737632b30eSBen Skeggs 
747632b30eSBen Skeggs 	switch (pll) {
757632b30eSBen Skeggs 	case 0x00e800:
767632b30eSBen Skeggs 	case 0x00e820:
77822ad79fSBen Skeggs 		sclk = device->crystal;
787632b30eSBen Skeggs 		P = 1;
797632b30eSBen Skeggs 		break;
807632b30eSBen Skeggs 	case 0x132000:
813eca809bSBen Skeggs 		sclk = read_pll(clk, 0x132020);
827632b30eSBen Skeggs 		P = (coef & 0x10000000) ? 2 : 1;
837632b30eSBen Skeggs 		break;
847632b30eSBen Skeggs 	case 0x132020:
853eca809bSBen Skeggs 		sclk = read_div(clk, 0, 0x137320, 0x137330);
86822ad79fSBen Skeggs 		fN   = nvkm_rd32(device, pll + 0x10) >> 16;
877632b30eSBen Skeggs 		break;
887632b30eSBen Skeggs 	case 0x137000:
897632b30eSBen Skeggs 	case 0x137020:
907632b30eSBen Skeggs 	case 0x137040:
917632b30eSBen Skeggs 	case 0x1370e0:
923eca809bSBen Skeggs 		sclk = read_div(clk, (pll & 0xff) / 0x20, 0x137120, 0x137140);
937632b30eSBen Skeggs 		break;
947632b30eSBen Skeggs 	default:
957632b30eSBen Skeggs 		return 0;
967632b30eSBen Skeggs 	}
977632b30eSBen Skeggs 
987632b30eSBen Skeggs 	if (P == 0)
997632b30eSBen Skeggs 		P = 1;
1007632b30eSBen Skeggs 
1017632b30eSBen Skeggs 	sclk = (sclk * N) + (((u16)(fN + 4096) * sclk) >> 13);
1027632b30eSBen Skeggs 	return sclk / (M * P);
1037632b30eSBen Skeggs }
1047632b30eSBen Skeggs 
1057632b30eSBen Skeggs static u32
read_div(struct gk104_clk * clk,int doff,u32 dsrc,u32 dctl)1063eca809bSBen Skeggs read_div(struct gk104_clk *clk, int doff, u32 dsrc, u32 dctl)
1077632b30eSBen Skeggs {
108822ad79fSBen Skeggs 	struct nvkm_device *device = clk->base.subdev.device;
109822ad79fSBen Skeggs 	u32 ssrc = nvkm_rd32(device, dsrc + (doff * 4));
110822ad79fSBen Skeggs 	u32 sctl = nvkm_rd32(device, dctl + (doff * 4));
1117632b30eSBen Skeggs 
1127632b30eSBen Skeggs 	switch (ssrc & 0x00000003) {
1137632b30eSBen Skeggs 	case 0:
1147632b30eSBen Skeggs 		if ((ssrc & 0x00030000) != 0x00030000)
115822ad79fSBen Skeggs 			return device->crystal;
1167632b30eSBen Skeggs 		return 108000;
1177632b30eSBen Skeggs 	case 2:
1187632b30eSBen Skeggs 		return 100000;
1197632b30eSBen Skeggs 	case 3:
1207632b30eSBen Skeggs 		if (sctl & 0x80000000) {
1213eca809bSBen Skeggs 			u32 sclk = read_vco(clk, dsrc + (doff * 4));
1227632b30eSBen Skeggs 			u32 sdiv = (sctl & 0x0000003f) + 2;
1237632b30eSBen Skeggs 			return (sclk * 2) / sdiv;
1247632b30eSBen Skeggs 		}
1257632b30eSBen Skeggs 
1263eca809bSBen Skeggs 		return read_vco(clk, dsrc + (doff * 4));
1277632b30eSBen Skeggs 	default:
1287632b30eSBen Skeggs 		return 0;
1297632b30eSBen Skeggs 	}
1307632b30eSBen Skeggs }
1317632b30eSBen Skeggs 
1327632b30eSBen Skeggs static u32
read_mem(struct gk104_clk * clk)1333eca809bSBen Skeggs read_mem(struct gk104_clk *clk)
1347632b30eSBen Skeggs {
135822ad79fSBen Skeggs 	struct nvkm_device *device = clk->base.subdev.device;
136822ad79fSBen Skeggs 	switch (nvkm_rd32(device, 0x1373f4) & 0x0000000f) {
1373eca809bSBen Skeggs 	case 1: return read_pll(clk, 0x132020);
1383eca809bSBen Skeggs 	case 2: return read_pll(clk, 0x132000);
1397632b30eSBen Skeggs 	default:
1407632b30eSBen Skeggs 		return 0;
1417632b30eSBen Skeggs 	}
1427632b30eSBen Skeggs }
1437632b30eSBen Skeggs 
1447632b30eSBen Skeggs static u32
read_clk(struct gk104_clk * clk,int idx)1453eca809bSBen Skeggs read_clk(struct gk104_clk *clk, int idx)
1467632b30eSBen Skeggs {
147822ad79fSBen Skeggs 	struct nvkm_device *device = clk->base.subdev.device;
148822ad79fSBen Skeggs 	u32 sctl = nvkm_rd32(device, 0x137250 + (idx * 4));
1497632b30eSBen Skeggs 	u32 sclk, sdiv;
1507632b30eSBen Skeggs 
1513eca809bSBen Skeggs 	if (idx < 7) {
152822ad79fSBen Skeggs 		u32 ssel = nvkm_rd32(device, 0x137100);
1533eca809bSBen Skeggs 		if (ssel & (1 << idx)) {
1543eca809bSBen Skeggs 			sclk = read_pll(clk, 0x137000 + (idx * 0x20));
1557632b30eSBen Skeggs 			sdiv = 1;
1567632b30eSBen Skeggs 		} else {
1573eca809bSBen Skeggs 			sclk = read_div(clk, idx, 0x137160, 0x1371d0);
1587632b30eSBen Skeggs 			sdiv = 0;
1597632b30eSBen Skeggs 		}
1607632b30eSBen Skeggs 	} else {
161822ad79fSBen Skeggs 		u32 ssrc = nvkm_rd32(device, 0x137160 + (idx * 0x04));
1627632b30eSBen Skeggs 		if ((ssrc & 0x00000003) == 0x00000003) {
1633eca809bSBen Skeggs 			sclk = read_div(clk, idx, 0x137160, 0x1371d0);
1647632b30eSBen Skeggs 			if (ssrc & 0x00000100) {
1657632b30eSBen Skeggs 				if (ssrc & 0x40000000)
1663eca809bSBen Skeggs 					sclk = read_pll(clk, 0x1370e0);
1677632b30eSBen Skeggs 				sdiv = 1;
1687632b30eSBen Skeggs 			} else {
1697632b30eSBen Skeggs 				sdiv = 0;
1707632b30eSBen Skeggs 			}
1717632b30eSBen Skeggs 		} else {
1723eca809bSBen Skeggs 			sclk = read_div(clk, idx, 0x137160, 0x1371d0);
1737632b30eSBen Skeggs 			sdiv = 0;
1747632b30eSBen Skeggs 		}
1757632b30eSBen Skeggs 	}
1767632b30eSBen Skeggs 
1777632b30eSBen Skeggs 	if (sctl & 0x80000000) {
1787632b30eSBen Skeggs 		if (sdiv)
1797632b30eSBen Skeggs 			sdiv = ((sctl & 0x00003f00) >> 8) + 2;
1807632b30eSBen Skeggs 		else
1817632b30eSBen Skeggs 			sdiv = ((sctl & 0x0000003f) >> 0) + 2;
1827632b30eSBen Skeggs 		return (sclk * 2) / sdiv;
1837632b30eSBen Skeggs 	}
1847632b30eSBen Skeggs 
1857632b30eSBen Skeggs 	return sclk;
1867632b30eSBen Skeggs }
1877632b30eSBen Skeggs 
1887632b30eSBen Skeggs static int
gk104_clk_read(struct nvkm_clk * base,enum nv_clk_src src)1896625f55cSBen Skeggs gk104_clk_read(struct nvkm_clk *base, enum nv_clk_src src)
1907632b30eSBen Skeggs {
1916625f55cSBen Skeggs 	struct gk104_clk *clk = gk104_clk(base);
192b907649eSBen Skeggs 	struct nvkm_subdev *subdev = &clk->base.subdev;
193b907649eSBen Skeggs 	struct nvkm_device *device = subdev->device;
1947632b30eSBen Skeggs 
1957632b30eSBen Skeggs 	switch (src) {
1967632b30eSBen Skeggs 	case nv_clk_src_crystal:
1977632b30eSBen Skeggs 		return device->crystal;
1987632b30eSBen Skeggs 	case nv_clk_src_href:
1997632b30eSBen Skeggs 		return 100000;
2007632b30eSBen Skeggs 	case nv_clk_src_mem:
2013eca809bSBen Skeggs 		return read_mem(clk);
2027632b30eSBen Skeggs 	case nv_clk_src_gpc:
2033eca809bSBen Skeggs 		return read_clk(clk, 0x00);
2047632b30eSBen Skeggs 	case nv_clk_src_rop:
2053eca809bSBen Skeggs 		return read_clk(clk, 0x01);
2067632b30eSBen Skeggs 	case nv_clk_src_hubk07:
2073eca809bSBen Skeggs 		return read_clk(clk, 0x02);
2087632b30eSBen Skeggs 	case nv_clk_src_hubk06:
2093eca809bSBen Skeggs 		return read_clk(clk, 0x07);
2107632b30eSBen Skeggs 	case nv_clk_src_hubk01:
2113eca809bSBen Skeggs 		return read_clk(clk, 0x08);
212547dd271SBen Skeggs 	case nv_clk_src_pmu:
2133eca809bSBen Skeggs 		return read_clk(clk, 0x0c);
2147632b30eSBen Skeggs 	case nv_clk_src_vdec:
2153eca809bSBen Skeggs 		return read_clk(clk, 0x0e);
2167632b30eSBen Skeggs 	default:
217b907649eSBen Skeggs 		nvkm_error(subdev, "invalid clock source %d\n", src);
2187632b30eSBen Skeggs 		return -EINVAL;
2197632b30eSBen Skeggs 	}
2207632b30eSBen Skeggs }
2217632b30eSBen Skeggs 
2227632b30eSBen Skeggs static u32
calc_div(struct gk104_clk * clk,int idx,u32 ref,u32 freq,u32 * ddiv)2233eca809bSBen Skeggs calc_div(struct gk104_clk *clk, int idx, u32 ref, u32 freq, u32 *ddiv)
2247632b30eSBen Skeggs {
2257632b30eSBen Skeggs 	u32 div = min((ref * 2) / freq, (u32)65);
2267632b30eSBen Skeggs 	if (div < 2)
2277632b30eSBen Skeggs 		div = 2;
2287632b30eSBen Skeggs 
2297632b30eSBen Skeggs 	*ddiv = div - 2;
2307632b30eSBen Skeggs 	return (ref * 2) / div;
2317632b30eSBen Skeggs }
2327632b30eSBen Skeggs 
2337632b30eSBen Skeggs static u32
calc_src(struct gk104_clk * clk,int idx,u32 freq,u32 * dsrc,u32 * ddiv)2343eca809bSBen Skeggs calc_src(struct gk104_clk *clk, int idx, u32 freq, u32 *dsrc, u32 *ddiv)
2357632b30eSBen Skeggs {
2367632b30eSBen Skeggs 	u32 sclk;
2377632b30eSBen Skeggs 
2387632b30eSBen Skeggs 	/* use one of the fixed frequencies if possible */
2397632b30eSBen Skeggs 	*ddiv = 0x00000000;
2407632b30eSBen Skeggs 	switch (freq) {
2417632b30eSBen Skeggs 	case  27000:
2427632b30eSBen Skeggs 	case 108000:
2437632b30eSBen Skeggs 		*dsrc = 0x00000000;
2447632b30eSBen Skeggs 		if (freq == 108000)
2457632b30eSBen Skeggs 			*dsrc |= 0x00030000;
2467632b30eSBen Skeggs 		return freq;
2477632b30eSBen Skeggs 	case 100000:
2487632b30eSBen Skeggs 		*dsrc = 0x00000002;
2497632b30eSBen Skeggs 		return freq;
2507632b30eSBen Skeggs 	default:
2517632b30eSBen Skeggs 		*dsrc = 0x00000003;
2527632b30eSBen Skeggs 		break;
2537632b30eSBen Skeggs 	}
2547632b30eSBen Skeggs 
2557632b30eSBen Skeggs 	/* otherwise, calculate the closest divider */
2563eca809bSBen Skeggs 	sclk = read_vco(clk, 0x137160 + (idx * 4));
2573eca809bSBen Skeggs 	if (idx < 7)
2583eca809bSBen Skeggs 		sclk = calc_div(clk, idx, sclk, freq, ddiv);
2597632b30eSBen Skeggs 	return sclk;
2607632b30eSBen Skeggs }
2617632b30eSBen Skeggs 
2627632b30eSBen Skeggs static u32
calc_pll(struct gk104_clk * clk,int idx,u32 freq,u32 * coef)2633eca809bSBen Skeggs calc_pll(struct gk104_clk *clk, int idx, u32 freq, u32 *coef)
2647632b30eSBen Skeggs {
26546484438SBen Skeggs 	struct nvkm_subdev *subdev = &clk->base.subdev;
26646484438SBen Skeggs 	struct nvkm_bios *bios = subdev->device->bios;
2677632b30eSBen Skeggs 	struct nvbios_pll limits;
2687632b30eSBen Skeggs 	int N, M, P, ret;
2697632b30eSBen Skeggs 
2703eca809bSBen Skeggs 	ret = nvbios_pll_parse(bios, 0x137000 + (idx * 0x20), &limits);
2717632b30eSBen Skeggs 	if (ret)
2727632b30eSBen Skeggs 		return 0;
2737632b30eSBen Skeggs 
2743eca809bSBen Skeggs 	limits.refclk = read_div(clk, idx, 0x137120, 0x137140);
2757632b30eSBen Skeggs 	if (!limits.refclk)
2767632b30eSBen Skeggs 		return 0;
2777632b30eSBen Skeggs 
27846484438SBen Skeggs 	ret = gt215_pll_calc(subdev, &limits, freq, &N, NULL, &M, &P);
2797632b30eSBen Skeggs 	if (ret <= 0)
2807632b30eSBen Skeggs 		return 0;
2817632b30eSBen Skeggs 
2827632b30eSBen Skeggs 	*coef = (P << 16) | (N << 8) | M;
2837632b30eSBen Skeggs 	return ret;
2847632b30eSBen Skeggs }
2857632b30eSBen Skeggs 
2867632b30eSBen Skeggs static int
calc_clk(struct gk104_clk * clk,struct nvkm_cstate * cstate,int idx,int dom)2873eca809bSBen Skeggs calc_clk(struct gk104_clk *clk,
2883eca809bSBen Skeggs 	 struct nvkm_cstate *cstate, int idx, int dom)
2897632b30eSBen Skeggs {
2903eca809bSBen Skeggs 	struct gk104_clk_info *info = &clk->eng[idx];
2917632b30eSBen Skeggs 	u32 freq = cstate->domain[dom];
2927632b30eSBen Skeggs 	u32 src0, div0, div1D, div1P = 0;
2937632b30eSBen Skeggs 	u32 clk0, clk1 = 0;
2947632b30eSBen Skeggs 
2957632b30eSBen Skeggs 	/* invalid clock domain */
2967632b30eSBen Skeggs 	if (!freq)
2977632b30eSBen Skeggs 		return 0;
2987632b30eSBen Skeggs 
2997632b30eSBen Skeggs 	/* first possible path, using only dividers */
3003eca809bSBen Skeggs 	clk0 = calc_src(clk, idx, freq, &src0, &div0);
3013eca809bSBen Skeggs 	clk0 = calc_div(clk, idx, clk0, freq, &div1D);
3027632b30eSBen Skeggs 
3037632b30eSBen Skeggs 	/* see if we can get any closer using PLLs */
3043eca809bSBen Skeggs 	if (clk0 != freq && (0x0000ff87 & (1 << idx))) {
3053eca809bSBen Skeggs 		if (idx <= 7)
3063eca809bSBen Skeggs 			clk1 = calc_pll(clk, idx, freq, &info->coef);
3077632b30eSBen Skeggs 		else
3087632b30eSBen Skeggs 			clk1 = cstate->domain[nv_clk_src_hubk06];
3093eca809bSBen Skeggs 		clk1 = calc_div(clk, idx, clk1, freq, &div1P);
3107632b30eSBen Skeggs 	}
3117632b30eSBen Skeggs 
3127632b30eSBen Skeggs 	/* select the method which gets closest to target freq */
3137632b30eSBen Skeggs 	if (abs((int)freq - clk0) <= abs((int)freq - clk1)) {
3147632b30eSBen Skeggs 		info->dsrc = src0;
3157632b30eSBen Skeggs 		if (div0) {
3167632b30eSBen Skeggs 			info->ddiv |= 0x80000000;
3177632b30eSBen Skeggs 			info->ddiv |= div0;
3187632b30eSBen Skeggs 		}
3197632b30eSBen Skeggs 		if (div1D) {
3207632b30eSBen Skeggs 			info->mdiv |= 0x80000000;
3217632b30eSBen Skeggs 			info->mdiv |= div1D;
3227632b30eSBen Skeggs 		}
3237632b30eSBen Skeggs 		info->ssel = 0;
3247632b30eSBen Skeggs 		info->freq = clk0;
3257632b30eSBen Skeggs 	} else {
3267632b30eSBen Skeggs 		if (div1P) {
3277632b30eSBen Skeggs 			info->mdiv |= 0x80000000;
3287632b30eSBen Skeggs 			info->mdiv |= div1P << 8;
3297632b30eSBen Skeggs 		}
3303eca809bSBen Skeggs 		info->ssel = (1 << idx);
3317632b30eSBen Skeggs 		info->dsrc = 0x40000100;
3327632b30eSBen Skeggs 		info->freq = clk1;
3337632b30eSBen Skeggs 	}
3347632b30eSBen Skeggs 
3357632b30eSBen Skeggs 	return 0;
3367632b30eSBen Skeggs }
3377632b30eSBen Skeggs 
3387632b30eSBen Skeggs static int
gk104_clk_calc(struct nvkm_clk * base,struct nvkm_cstate * cstate)3396625f55cSBen Skeggs gk104_clk_calc(struct nvkm_clk *base, struct nvkm_cstate *cstate)
3407632b30eSBen Skeggs {
3416625f55cSBen Skeggs 	struct gk104_clk *clk = gk104_clk(base);
3427632b30eSBen Skeggs 	int ret;
3437632b30eSBen Skeggs 
3443eca809bSBen Skeggs 	if ((ret = calc_clk(clk, cstate, 0x00, nv_clk_src_gpc)) ||
3453eca809bSBen Skeggs 	    (ret = calc_clk(clk, cstate, 0x01, nv_clk_src_rop)) ||
3463eca809bSBen Skeggs 	    (ret = calc_clk(clk, cstate, 0x02, nv_clk_src_hubk07)) ||
3473eca809bSBen Skeggs 	    (ret = calc_clk(clk, cstate, 0x07, nv_clk_src_hubk06)) ||
3483eca809bSBen Skeggs 	    (ret = calc_clk(clk, cstate, 0x08, nv_clk_src_hubk01)) ||
349547dd271SBen Skeggs 	    (ret = calc_clk(clk, cstate, 0x0c, nv_clk_src_pmu)) ||
3503eca809bSBen Skeggs 	    (ret = calc_clk(clk, cstate, 0x0e, nv_clk_src_vdec)))
3517632b30eSBen Skeggs 		return ret;
3527632b30eSBen Skeggs 
3537632b30eSBen Skeggs 	return 0;
3547632b30eSBen Skeggs }
3557632b30eSBen Skeggs 
3567632b30eSBen Skeggs static void
gk104_clk_prog_0(struct gk104_clk * clk,int idx)3573eca809bSBen Skeggs gk104_clk_prog_0(struct gk104_clk *clk, int idx)
3587632b30eSBen Skeggs {
3593eca809bSBen Skeggs 	struct gk104_clk_info *info = &clk->eng[idx];
360822ad79fSBen Skeggs 	struct nvkm_device *device = clk->base.subdev.device;
3617632b30eSBen Skeggs 	if (!info->ssel) {
362822ad79fSBen Skeggs 		nvkm_mask(device, 0x1371d0 + (idx * 0x04), 0x8000003f, info->ddiv);
363822ad79fSBen Skeggs 		nvkm_wr32(device, 0x137160 + (idx * 0x04), info->dsrc);
3647632b30eSBen Skeggs 	}
3657632b30eSBen Skeggs }
3667632b30eSBen Skeggs 
3677632b30eSBen Skeggs static void
gk104_clk_prog_1_0(struct gk104_clk * clk,int idx)3683eca809bSBen Skeggs gk104_clk_prog_1_0(struct gk104_clk *clk, int idx)
3697632b30eSBen Skeggs {
370822ad79fSBen Skeggs 	struct nvkm_device *device = clk->base.subdev.device;
371822ad79fSBen Skeggs 	nvkm_mask(device, 0x137100, (1 << idx), 0x00000000);
3726979c630SBen Skeggs 	nvkm_msec(device, 2000,
3736979c630SBen Skeggs 		if (!(nvkm_rd32(device, 0x137100) & (1 << idx)))
3746979c630SBen Skeggs 			break;
3756979c630SBen Skeggs 	);
3767632b30eSBen Skeggs }
3777632b30eSBen Skeggs 
3787632b30eSBen Skeggs static void
gk104_clk_prog_1_1(struct gk104_clk * clk,int idx)3793eca809bSBen Skeggs gk104_clk_prog_1_1(struct gk104_clk *clk, int idx)
3807632b30eSBen Skeggs {
381822ad79fSBen Skeggs 	struct nvkm_device *device = clk->base.subdev.device;
382822ad79fSBen Skeggs 	nvkm_mask(device, 0x137160 + (idx * 0x04), 0x00000100, 0x00000000);
3837632b30eSBen Skeggs }
3847632b30eSBen Skeggs 
3857632b30eSBen Skeggs static void
gk104_clk_prog_2(struct gk104_clk * clk,int idx)3863eca809bSBen Skeggs gk104_clk_prog_2(struct gk104_clk *clk, int idx)
3877632b30eSBen Skeggs {
3883eca809bSBen Skeggs 	struct gk104_clk_info *info = &clk->eng[idx];
389822ad79fSBen Skeggs 	struct nvkm_device *device = clk->base.subdev.device;
3903eca809bSBen Skeggs 	const u32 addr = 0x137000 + (idx * 0x20);
391822ad79fSBen Skeggs 	nvkm_mask(device, addr + 0x00, 0x00000004, 0x00000000);
392822ad79fSBen Skeggs 	nvkm_mask(device, addr + 0x00, 0x00000001, 0x00000000);
3937632b30eSBen Skeggs 	if (info->coef) {
394822ad79fSBen Skeggs 		nvkm_wr32(device, addr + 0x04, info->coef);
395822ad79fSBen Skeggs 		nvkm_mask(device, addr + 0x00, 0x00000001, 0x00000001);
3960f7fbb99SRoy Spliet 
3970f7fbb99SRoy Spliet 		/* Test PLL lock */
3980f7fbb99SRoy Spliet 		nvkm_mask(device, addr + 0x00, 0x00000010, 0x00000000);
3996979c630SBen Skeggs 		nvkm_msec(device, 2000,
4006979c630SBen Skeggs 			if (nvkm_rd32(device, addr + 0x00) & 0x00020000)
4016979c630SBen Skeggs 				break;
4026979c630SBen Skeggs 		);
4030f7fbb99SRoy Spliet 		nvkm_mask(device, addr + 0x00, 0x00000010, 0x00000010);
4040f7fbb99SRoy Spliet 
4050f7fbb99SRoy Spliet 		/* Enable sync mode */
4060f7fbb99SRoy Spliet 		nvkm_mask(device, addr + 0x00, 0x00000004, 0x00000004);
4077632b30eSBen Skeggs 	}
4087632b30eSBen Skeggs }
4097632b30eSBen Skeggs 
4107632b30eSBen Skeggs static void
gk104_clk_prog_3(struct gk104_clk * clk,int idx)4113eca809bSBen Skeggs gk104_clk_prog_3(struct gk104_clk *clk, int idx)
4127632b30eSBen Skeggs {
4133eca809bSBen Skeggs 	struct gk104_clk_info *info = &clk->eng[idx];
414822ad79fSBen Skeggs 	struct nvkm_device *device = clk->base.subdev.device;
4157632b30eSBen Skeggs 	if (info->ssel)
416822ad79fSBen Skeggs 		nvkm_mask(device, 0x137250 + (idx * 0x04), 0x00003f00, info->mdiv);
4177632b30eSBen Skeggs 	else
418822ad79fSBen Skeggs 		nvkm_mask(device, 0x137250 + (idx * 0x04), 0x0000003f, info->mdiv);
4197632b30eSBen Skeggs }
4207632b30eSBen Skeggs 
4217632b30eSBen Skeggs static void
gk104_clk_prog_4_0(struct gk104_clk * clk,int idx)4223eca809bSBen Skeggs gk104_clk_prog_4_0(struct gk104_clk *clk, int idx)
4237632b30eSBen Skeggs {
4243eca809bSBen Skeggs 	struct gk104_clk_info *info = &clk->eng[idx];
425822ad79fSBen Skeggs 	struct nvkm_device *device = clk->base.subdev.device;
4267632b30eSBen Skeggs 	if (info->ssel) {
427822ad79fSBen Skeggs 		nvkm_mask(device, 0x137100, (1 << idx), info->ssel);
4286979c630SBen Skeggs 		nvkm_msec(device, 2000,
4296979c630SBen Skeggs 			u32 tmp = nvkm_rd32(device, 0x137100) & (1 << idx);
4306979c630SBen Skeggs 			if (tmp == info->ssel)
4316979c630SBen Skeggs 				break;
4326979c630SBen Skeggs 		);
4337632b30eSBen Skeggs 	}
4347632b30eSBen Skeggs }
4357632b30eSBen Skeggs 
4367632b30eSBen Skeggs static void
gk104_clk_prog_4_1(struct gk104_clk * clk,int idx)4373eca809bSBen Skeggs gk104_clk_prog_4_1(struct gk104_clk *clk, int idx)
4387632b30eSBen Skeggs {
4393eca809bSBen Skeggs 	struct gk104_clk_info *info = &clk->eng[idx];
440822ad79fSBen Skeggs 	struct nvkm_device *device = clk->base.subdev.device;
4417632b30eSBen Skeggs 	if (info->ssel) {
442822ad79fSBen Skeggs 		nvkm_mask(device, 0x137160 + (idx * 0x04), 0x40000000, 0x40000000);
443822ad79fSBen Skeggs 		nvkm_mask(device, 0x137160 + (idx * 0x04), 0x00000100, 0x00000100);
4447632b30eSBen Skeggs 	}
4457632b30eSBen Skeggs }
4467632b30eSBen Skeggs 
4477632b30eSBen Skeggs static int
gk104_clk_prog(struct nvkm_clk * base)4486625f55cSBen Skeggs gk104_clk_prog(struct nvkm_clk *base)
4497632b30eSBen Skeggs {
4506625f55cSBen Skeggs 	struct gk104_clk *clk = gk104_clk(base);
4517632b30eSBen Skeggs 	struct {
4527632b30eSBen Skeggs 		u32 mask;
4533eca809bSBen Skeggs 		void (*exec)(struct gk104_clk *, int);
4547632b30eSBen Skeggs 	} stage[] = {
4557632b30eSBen Skeggs 		{ 0x007f, gk104_clk_prog_0   }, /* div programming */
4567632b30eSBen Skeggs 		{ 0x007f, gk104_clk_prog_1_0 }, /* select div mode */
4577632b30eSBen Skeggs 		{ 0xff80, gk104_clk_prog_1_1 },
4587632b30eSBen Skeggs 		{ 0x00ff, gk104_clk_prog_2   }, /* (maybe) program pll */
4597632b30eSBen Skeggs 		{ 0xff80, gk104_clk_prog_3   }, /* final divider */
4607632b30eSBen Skeggs 		{ 0x007f, gk104_clk_prog_4_0 }, /* (maybe) select pll mode */
4617632b30eSBen Skeggs 		{ 0xff80, gk104_clk_prog_4_1 },
4627632b30eSBen Skeggs 	};
4637632b30eSBen Skeggs 	int i, j;
4647632b30eSBen Skeggs 
4657632b30eSBen Skeggs 	for (i = 0; i < ARRAY_SIZE(stage); i++) {
4663eca809bSBen Skeggs 		for (j = 0; j < ARRAY_SIZE(clk->eng); j++) {
4677632b30eSBen Skeggs 			if (!(stage[i].mask & (1 << j)))
4687632b30eSBen Skeggs 				continue;
4693eca809bSBen Skeggs 			if (!clk->eng[j].freq)
4707632b30eSBen Skeggs 				continue;
4713eca809bSBen Skeggs 			stage[i].exec(clk, j);
4727632b30eSBen Skeggs 		}
4737632b30eSBen Skeggs 	}
4747632b30eSBen Skeggs 
4757632b30eSBen Skeggs 	return 0;
4767632b30eSBen Skeggs }
4777632b30eSBen Skeggs 
4787632b30eSBen Skeggs static void
gk104_clk_tidy(struct nvkm_clk * base)4796625f55cSBen Skeggs gk104_clk_tidy(struct nvkm_clk *base)
4807632b30eSBen Skeggs {
4816625f55cSBen Skeggs 	struct gk104_clk *clk = gk104_clk(base);
4823eca809bSBen Skeggs 	memset(clk->eng, 0x00, sizeof(clk->eng));
4837632b30eSBen Skeggs }
4847632b30eSBen Skeggs 
4856625f55cSBen Skeggs static const struct nvkm_clk_func
4866625f55cSBen Skeggs gk104_clk = {
4876625f55cSBen Skeggs 	.read = gk104_clk_read,
4886625f55cSBen Skeggs 	.calc = gk104_clk_calc,
4896625f55cSBen Skeggs 	.prog = gk104_clk_prog,
4906625f55cSBen Skeggs 	.tidy = gk104_clk_tidy,
4916625f55cSBen Skeggs 	.domains = {
4927632b30eSBen Skeggs 		{ nv_clk_src_crystal, 0xff },
4937632b30eSBen Skeggs 		{ nv_clk_src_href   , 0xff },
4944b9ce6e7SKarol Herbst 		{ nv_clk_src_gpc    , 0x00, NVKM_CLK_DOM_FLAG_CORE | NVKM_CLK_DOM_FLAG_VPSTATE, "core", 2000 },
4957632b30eSBen Skeggs 		{ nv_clk_src_hubk07 , 0x01, NVKM_CLK_DOM_FLAG_CORE },
4967632b30eSBen Skeggs 		{ nv_clk_src_rop    , 0x02, NVKM_CLK_DOM_FLAG_CORE },
4977632b30eSBen Skeggs 		{ nv_clk_src_mem    , 0x03, 0, "memory", 500 },
4987632b30eSBen Skeggs 		{ nv_clk_src_hubk06 , 0x04, NVKM_CLK_DOM_FLAG_CORE },
4997632b30eSBen Skeggs 		{ nv_clk_src_hubk01 , 0x05 },
5007632b30eSBen Skeggs 		{ nv_clk_src_vdec   , 0x06 },
501547dd271SBen Skeggs 		{ nv_clk_src_pmu    , 0x07 },
5027632b30eSBen Skeggs 		{ nv_clk_src_max }
5036625f55cSBen Skeggs 	}
5047632b30eSBen Skeggs };
5057632b30eSBen Skeggs 
5066625f55cSBen Skeggs int
gk104_clk_new(struct nvkm_device * device,enum nvkm_subdev_type type,int inst,struct nvkm_clk ** pclk)507*98fd7f83SBen Skeggs gk104_clk_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
508*98fd7f83SBen Skeggs 	      struct nvkm_clk **pclk)
5097632b30eSBen Skeggs {
5103eca809bSBen Skeggs 	struct gk104_clk *clk;
5117632b30eSBen Skeggs 
5126625f55cSBen Skeggs 	if (!(clk = kzalloc(sizeof(*clk), GFP_KERNEL)))
5136625f55cSBen Skeggs 		return -ENOMEM;
5146625f55cSBen Skeggs 	*pclk = &clk->base;
5157632b30eSBen Skeggs 
516*98fd7f83SBen Skeggs 	return nvkm_clk_ctor(&gk104_clk, device, type, inst, true, &clk->base);
5177632b30eSBen Skeggs }
518