17632b30eSBen Skeggs /*
27632b30eSBen Skeggs  * Copyright 2012 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 gf100_clk(p) container_of((p), struct gf100_clk, base)
256625f55cSBen Skeggs #include "priv.h"
267632b30eSBen Skeggs #include "pll.h"
277632b30eSBen Skeggs 
287632b30eSBen Skeggs #include <subdev/bios.h>
297632b30eSBen Skeggs #include <subdev/bios/pll.h>
307632b30eSBen Skeggs #include <subdev/timer.h>
317632b30eSBen Skeggs 
327632b30eSBen Skeggs struct gf100_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 gf100_clk {
427632b30eSBen Skeggs 	struct nvkm_clk base;
437632b30eSBen Skeggs 	struct gf100_clk_info eng[16];
447632b30eSBen Skeggs };
457632b30eSBen Skeggs 
463eca809bSBen Skeggs static u32 read_div(struct gf100_clk *, int, u32, u32);
477632b30eSBen Skeggs 
487632b30eSBen Skeggs static u32
read_vco(struct gf100_clk * clk,u32 dsrc)493eca809bSBen Skeggs read_vco(struct gf100_clk *clk, u32 dsrc)
507632b30eSBen Skeggs {
51822ad79fSBen Skeggs 	struct nvkm_device *device = clk->base.subdev.device;
52822ad79fSBen Skeggs 	u32 ssrc = nvkm_rd32(device, dsrc);
537632b30eSBen Skeggs 	if (!(ssrc & 0x00000100))
546625f55cSBen Skeggs 		return nvkm_clk_read(&clk->base, nv_clk_src_sppll0);
556625f55cSBen Skeggs 	return nvkm_clk_read(&clk->base, nv_clk_src_sppll1);
567632b30eSBen Skeggs }
577632b30eSBen Skeggs 
587632b30eSBen Skeggs static u32
read_pll(struct gf100_clk * clk,u32 pll)593eca809bSBen Skeggs read_pll(struct gf100_clk *clk, u32 pll)
607632b30eSBen Skeggs {
61822ad79fSBen Skeggs 	struct nvkm_device *device = clk->base.subdev.device;
62822ad79fSBen Skeggs 	u32 ctrl = nvkm_rd32(device, pll + 0x00);
63822ad79fSBen Skeggs 	u32 coef = nvkm_rd32(device, pll + 0x04);
647632b30eSBen Skeggs 	u32 P = (coef & 0x003f0000) >> 16;
657632b30eSBen Skeggs 	u32 N = (coef & 0x0000ff00) >> 8;
667632b30eSBen Skeggs 	u32 M = (coef & 0x000000ff) >> 0;
677632b30eSBen Skeggs 	u32 sclk;
687632b30eSBen Skeggs 
697632b30eSBen Skeggs 	if (!(ctrl & 0x00000001))
707632b30eSBen Skeggs 		return 0;
717632b30eSBen Skeggs 
727632b30eSBen Skeggs 	switch (pll) {
737632b30eSBen Skeggs 	case 0x00e800:
747632b30eSBen Skeggs 	case 0x00e820:
75822ad79fSBen Skeggs 		sclk = device->crystal;
767632b30eSBen Skeggs 		P = 1;
777632b30eSBen Skeggs 		break;
787632b30eSBen Skeggs 	case 0x132000:
796625f55cSBen Skeggs 		sclk = nvkm_clk_read(&clk->base, nv_clk_src_mpllsrc);
807632b30eSBen Skeggs 		break;
817632b30eSBen Skeggs 	case 0x132020:
826625f55cSBen Skeggs 		sclk = nvkm_clk_read(&clk->base, nv_clk_src_mpllsrcref);
837632b30eSBen Skeggs 		break;
847632b30eSBen Skeggs 	case 0x137000:
857632b30eSBen Skeggs 	case 0x137020:
867632b30eSBen Skeggs 	case 0x137040:
877632b30eSBen Skeggs 	case 0x1370e0:
883eca809bSBen Skeggs 		sclk = read_div(clk, (pll & 0xff) / 0x20, 0x137120, 0x137140);
897632b30eSBen Skeggs 		break;
907632b30eSBen Skeggs 	default:
917632b30eSBen Skeggs 		return 0;
927632b30eSBen Skeggs 	}
937632b30eSBen Skeggs 
947632b30eSBen Skeggs 	return sclk * N / M / P;
957632b30eSBen Skeggs }
967632b30eSBen Skeggs 
977632b30eSBen Skeggs static u32
read_div(struct gf100_clk * clk,int doff,u32 dsrc,u32 dctl)983eca809bSBen Skeggs read_div(struct gf100_clk *clk, int doff, u32 dsrc, u32 dctl)
997632b30eSBen Skeggs {
100822ad79fSBen Skeggs 	struct nvkm_device *device = clk->base.subdev.device;
101822ad79fSBen Skeggs 	u32 ssrc = nvkm_rd32(device, dsrc + (doff * 4));
102f8fa2e4bSRoy Spliet 	u32 sclk, sctl, sdiv = 2;
1037632b30eSBen Skeggs 
1047632b30eSBen Skeggs 	switch (ssrc & 0x00000003) {
1057632b30eSBen Skeggs 	case 0:
1067632b30eSBen Skeggs 		if ((ssrc & 0x00030000) != 0x00030000)
107822ad79fSBen Skeggs 			return device->crystal;
1087632b30eSBen Skeggs 		return 108000;
1097632b30eSBen Skeggs 	case 2:
1107632b30eSBen Skeggs 		return 100000;
1117632b30eSBen Skeggs 	case 3:
112f8fa2e4bSRoy Spliet 		sclk = read_vco(clk, dsrc + (doff * 4));
113f8fa2e4bSRoy Spliet 
114f8fa2e4bSRoy Spliet 		/* Memclk has doff of 0 despite its alt. location */
115f8fa2e4bSRoy Spliet 		if (doff <= 2) {
116f8fa2e4bSRoy Spliet 			sctl = nvkm_rd32(device, dctl + (doff * 4));
117f8fa2e4bSRoy Spliet 
1187632b30eSBen Skeggs 			if (sctl & 0x80000000) {
119f8fa2e4bSRoy Spliet 				if (ssrc & 0x100)
120f8fa2e4bSRoy Spliet 					sctl >>= 8;
121f8fa2e4bSRoy Spliet 
122f8fa2e4bSRoy Spliet 				sdiv = (sctl & 0x3f) + 2;
123f8fa2e4bSRoy Spliet 			}
1247632b30eSBen Skeggs 		}
1257632b30eSBen Skeggs 
126f8fa2e4bSRoy Spliet 		return (sclk * 2) / sdiv;
1277632b30eSBen Skeggs 	default:
1287632b30eSBen Skeggs 		return 0;
1297632b30eSBen Skeggs 	}
1307632b30eSBen Skeggs }
1317632b30eSBen Skeggs 
1327632b30eSBen Skeggs static u32
read_clk(struct gf100_clk * clk,int idx)1333eca809bSBen Skeggs read_clk(struct gf100_clk *clk, int idx)
1347632b30eSBen Skeggs {
135822ad79fSBen Skeggs 	struct nvkm_device *device = clk->base.subdev.device;
136822ad79fSBen Skeggs 	u32 sctl = nvkm_rd32(device, 0x137250 + (idx * 4));
137822ad79fSBen Skeggs 	u32 ssel = nvkm_rd32(device, 0x137100);
1387632b30eSBen Skeggs 	u32 sclk, sdiv;
1397632b30eSBen Skeggs 
1403eca809bSBen Skeggs 	if (ssel & (1 << idx)) {
1413eca809bSBen Skeggs 		if (idx < 7)
1423eca809bSBen Skeggs 			sclk = read_pll(clk, 0x137000 + (idx * 0x20));
1437632b30eSBen Skeggs 		else
1443eca809bSBen Skeggs 			sclk = read_pll(clk, 0x1370e0);
1457632b30eSBen Skeggs 		sdiv = ((sctl & 0x00003f00) >> 8) + 2;
1467632b30eSBen Skeggs 	} else {
1473eca809bSBen Skeggs 		sclk = read_div(clk, idx, 0x137160, 0x1371d0);
1487632b30eSBen Skeggs 		sdiv = ((sctl & 0x0000003f) >> 0) + 2;
1497632b30eSBen Skeggs 	}
1507632b30eSBen Skeggs 
1517632b30eSBen Skeggs 	if (sctl & 0x80000000)
1527632b30eSBen Skeggs 		return (sclk * 2) / sdiv;
1537632b30eSBen Skeggs 
1547632b30eSBen Skeggs 	return sclk;
1557632b30eSBen Skeggs }
1567632b30eSBen Skeggs 
1577632b30eSBen Skeggs static int
gf100_clk_read(struct nvkm_clk * base,enum nv_clk_src src)1586625f55cSBen Skeggs gf100_clk_read(struct nvkm_clk *base, enum nv_clk_src src)
1597632b30eSBen Skeggs {
1606625f55cSBen Skeggs 	struct gf100_clk *clk = gf100_clk(base);
161b907649eSBen Skeggs 	struct nvkm_subdev *subdev = &clk->base.subdev;
162b907649eSBen Skeggs 	struct nvkm_device *device = subdev->device;
1637632b30eSBen Skeggs 
1647632b30eSBen Skeggs 	switch (src) {
1657632b30eSBen Skeggs 	case nv_clk_src_crystal:
1667632b30eSBen Skeggs 		return device->crystal;
1677632b30eSBen Skeggs 	case nv_clk_src_href:
1687632b30eSBen Skeggs 		return 100000;
1697632b30eSBen Skeggs 	case nv_clk_src_sppll0:
1703eca809bSBen Skeggs 		return read_pll(clk, 0x00e800);
1717632b30eSBen Skeggs 	case nv_clk_src_sppll1:
1723eca809bSBen Skeggs 		return read_pll(clk, 0x00e820);
1737632b30eSBen Skeggs 
1747632b30eSBen Skeggs 	case nv_clk_src_mpllsrcref:
1753eca809bSBen Skeggs 		return read_div(clk, 0, 0x137320, 0x137330);
1767632b30eSBen Skeggs 	case nv_clk_src_mpllsrc:
1773eca809bSBen Skeggs 		return read_pll(clk, 0x132020);
1787632b30eSBen Skeggs 	case nv_clk_src_mpll:
1793eca809bSBen Skeggs 		return read_pll(clk, 0x132000);
1807632b30eSBen Skeggs 	case nv_clk_src_mdiv:
1813eca809bSBen Skeggs 		return read_div(clk, 0, 0x137300, 0x137310);
1827632b30eSBen Skeggs 	case nv_clk_src_mem:
183822ad79fSBen Skeggs 		if (nvkm_rd32(device, 0x1373f0) & 0x00000002)
1846625f55cSBen Skeggs 			return nvkm_clk_read(&clk->base, nv_clk_src_mpll);
1856625f55cSBen Skeggs 		return nvkm_clk_read(&clk->base, nv_clk_src_mdiv);
1867632b30eSBen Skeggs 
1877632b30eSBen Skeggs 	case nv_clk_src_gpc:
1883eca809bSBen Skeggs 		return read_clk(clk, 0x00);
1897632b30eSBen Skeggs 	case nv_clk_src_rop:
1903eca809bSBen Skeggs 		return read_clk(clk, 0x01);
1917632b30eSBen Skeggs 	case nv_clk_src_hubk07:
1923eca809bSBen Skeggs 		return read_clk(clk, 0x02);
1937632b30eSBen Skeggs 	case nv_clk_src_hubk06:
1943eca809bSBen Skeggs 		return read_clk(clk, 0x07);
1957632b30eSBen Skeggs 	case nv_clk_src_hubk01:
1963eca809bSBen Skeggs 		return read_clk(clk, 0x08);
1977632b30eSBen Skeggs 	case nv_clk_src_copy:
1983eca809bSBen Skeggs 		return read_clk(clk, 0x09);
199547dd271SBen Skeggs 	case nv_clk_src_pmu:
2003eca809bSBen Skeggs 		return read_clk(clk, 0x0c);
2017632b30eSBen Skeggs 	case nv_clk_src_vdec:
2023eca809bSBen Skeggs 		return read_clk(clk, 0x0e);
2037632b30eSBen Skeggs 	default:
204b907649eSBen Skeggs 		nvkm_error(subdev, "invalid clock source %d\n", src);
2057632b30eSBen Skeggs 		return -EINVAL;
2067632b30eSBen Skeggs 	}
2077632b30eSBen Skeggs }
2087632b30eSBen Skeggs 
2097632b30eSBen Skeggs static u32
calc_div(struct gf100_clk * clk,int idx,u32 ref,u32 freq,u32 * ddiv)2103eca809bSBen Skeggs calc_div(struct gf100_clk *clk, int idx, u32 ref, u32 freq, u32 *ddiv)
2117632b30eSBen Skeggs {
2127632b30eSBen Skeggs 	u32 div = min((ref * 2) / freq, (u32)65);
2137632b30eSBen Skeggs 	if (div < 2)
2147632b30eSBen Skeggs 		div = 2;
2157632b30eSBen Skeggs 
2167632b30eSBen Skeggs 	*ddiv = div - 2;
2177632b30eSBen Skeggs 	return (ref * 2) / div;
2187632b30eSBen Skeggs }
2197632b30eSBen Skeggs 
2207632b30eSBen Skeggs static u32
calc_src(struct gf100_clk * clk,int idx,u32 freq,u32 * dsrc,u32 * ddiv)2213eca809bSBen Skeggs calc_src(struct gf100_clk *clk, int idx, u32 freq, u32 *dsrc, u32 *ddiv)
2227632b30eSBen Skeggs {
2237632b30eSBen Skeggs 	u32 sclk;
2247632b30eSBen Skeggs 
2257632b30eSBen Skeggs 	/* use one of the fixed frequencies if possible */
2267632b30eSBen Skeggs 	*ddiv = 0x00000000;
2277632b30eSBen Skeggs 	switch (freq) {
2287632b30eSBen Skeggs 	case  27000:
2297632b30eSBen Skeggs 	case 108000:
2307632b30eSBen Skeggs 		*dsrc = 0x00000000;
2317632b30eSBen Skeggs 		if (freq == 108000)
2327632b30eSBen Skeggs 			*dsrc |= 0x00030000;
2337632b30eSBen Skeggs 		return freq;
2347632b30eSBen Skeggs 	case 100000:
2357632b30eSBen Skeggs 		*dsrc = 0x00000002;
2367632b30eSBen Skeggs 		return freq;
2377632b30eSBen Skeggs 	default:
2387632b30eSBen Skeggs 		*dsrc = 0x00000003;
2397632b30eSBen Skeggs 		break;
2407632b30eSBen Skeggs 	}
2417632b30eSBen Skeggs 
2427632b30eSBen Skeggs 	/* otherwise, calculate the closest divider */
2433eca809bSBen Skeggs 	sclk = read_vco(clk, 0x137160 + (idx * 4));
2443eca809bSBen Skeggs 	if (idx < 7)
2453eca809bSBen Skeggs 		sclk = calc_div(clk, idx, sclk, freq, ddiv);
2467632b30eSBen Skeggs 	return sclk;
2477632b30eSBen Skeggs }
2487632b30eSBen Skeggs 
2497632b30eSBen Skeggs static u32
calc_pll(struct gf100_clk * clk,int idx,u32 freq,u32 * coef)2503eca809bSBen Skeggs calc_pll(struct gf100_clk *clk, int idx, u32 freq, u32 *coef)
2517632b30eSBen Skeggs {
25246484438SBen Skeggs 	struct nvkm_subdev *subdev = &clk->base.subdev;
25346484438SBen Skeggs 	struct nvkm_bios *bios = subdev->device->bios;
2547632b30eSBen Skeggs 	struct nvbios_pll limits;
2557632b30eSBen Skeggs 	int N, M, P, ret;
2567632b30eSBen Skeggs 
2573eca809bSBen Skeggs 	ret = nvbios_pll_parse(bios, 0x137000 + (idx * 0x20), &limits);
2587632b30eSBen Skeggs 	if (ret)
2597632b30eSBen Skeggs 		return 0;
2607632b30eSBen Skeggs 
2613eca809bSBen Skeggs 	limits.refclk = read_div(clk, idx, 0x137120, 0x137140);
2627632b30eSBen Skeggs 	if (!limits.refclk)
2637632b30eSBen Skeggs 		return 0;
2647632b30eSBen Skeggs 
26546484438SBen Skeggs 	ret = gt215_pll_calc(subdev, &limits, freq, &N, NULL, &M, &P);
2667632b30eSBen Skeggs 	if (ret <= 0)
2677632b30eSBen Skeggs 		return 0;
2687632b30eSBen Skeggs 
2697632b30eSBen Skeggs 	*coef = (P << 16) | (N << 8) | M;
2707632b30eSBen Skeggs 	return ret;
2717632b30eSBen Skeggs }
2727632b30eSBen Skeggs 
2737632b30eSBen Skeggs static int
calc_clk(struct gf100_clk * clk,struct nvkm_cstate * cstate,int idx,int dom)2743eca809bSBen Skeggs calc_clk(struct gf100_clk *clk, struct nvkm_cstate *cstate, int idx, int dom)
2757632b30eSBen Skeggs {
2763eca809bSBen Skeggs 	struct gf100_clk_info *info = &clk->eng[idx];
2777632b30eSBen Skeggs 	u32 freq = cstate->domain[dom];
2787632b30eSBen Skeggs 	u32 src0, div0, div1D, div1P = 0;
2797632b30eSBen Skeggs 	u32 clk0, clk1 = 0;
2807632b30eSBen Skeggs 
2817632b30eSBen Skeggs 	/* invalid clock domain */
2827632b30eSBen Skeggs 	if (!freq)
2837632b30eSBen Skeggs 		return 0;
2847632b30eSBen Skeggs 
2857632b30eSBen Skeggs 	/* first possible path, using only dividers */
2863eca809bSBen Skeggs 	clk0 = calc_src(clk, idx, freq, &src0, &div0);
2873eca809bSBen Skeggs 	clk0 = calc_div(clk, idx, clk0, freq, &div1D);
2887632b30eSBen Skeggs 
2897632b30eSBen Skeggs 	/* see if we can get any closer using PLLs */
2903eca809bSBen Skeggs 	if (clk0 != freq && (0x00004387 & (1 << idx))) {
2913eca809bSBen Skeggs 		if (idx <= 7)
2923eca809bSBen Skeggs 			clk1 = calc_pll(clk, idx, freq, &info->coef);
2937632b30eSBen Skeggs 		else
2947632b30eSBen Skeggs 			clk1 = cstate->domain[nv_clk_src_hubk06];
2953eca809bSBen Skeggs 		clk1 = calc_div(clk, idx, clk1, freq, &div1P);
2967632b30eSBen Skeggs 	}
2977632b30eSBen Skeggs 
2987632b30eSBen Skeggs 	/* select the method which gets closest to target freq */
2997632b30eSBen Skeggs 	if (abs((int)freq - clk0) <= abs((int)freq - clk1)) {
3007632b30eSBen Skeggs 		info->dsrc = src0;
3017632b30eSBen Skeggs 		if (div0) {
3027632b30eSBen Skeggs 			info->ddiv |= 0x80000000;
3037632b30eSBen Skeggs 			info->ddiv |= div0 << 8;
3047632b30eSBen Skeggs 			info->ddiv |= div0;
3057632b30eSBen Skeggs 		}
3067632b30eSBen Skeggs 		if (div1D) {
3077632b30eSBen Skeggs 			info->mdiv |= 0x80000000;
3087632b30eSBen Skeggs 			info->mdiv |= div1D;
3097632b30eSBen Skeggs 		}
3107632b30eSBen Skeggs 		info->ssel = info->coef = 0;
3117632b30eSBen Skeggs 		info->freq = clk0;
3127632b30eSBen Skeggs 	} else {
3137632b30eSBen Skeggs 		if (div1P) {
3147632b30eSBen Skeggs 			info->mdiv |= 0x80000000;
3157632b30eSBen Skeggs 			info->mdiv |= div1P << 8;
3167632b30eSBen Skeggs 		}
3173eca809bSBen Skeggs 		info->ssel = (1 << idx);
3187632b30eSBen Skeggs 		info->freq = clk1;
3197632b30eSBen Skeggs 	}
3207632b30eSBen Skeggs 
3217632b30eSBen Skeggs 	return 0;
3227632b30eSBen Skeggs }
3237632b30eSBen Skeggs 
3247632b30eSBen Skeggs static int
gf100_clk_calc(struct nvkm_clk * base,struct nvkm_cstate * cstate)3256625f55cSBen Skeggs gf100_clk_calc(struct nvkm_clk *base, struct nvkm_cstate *cstate)
3267632b30eSBen Skeggs {
3276625f55cSBen Skeggs 	struct gf100_clk *clk = gf100_clk(base);
3287632b30eSBen Skeggs 	int ret;
3297632b30eSBen Skeggs 
3303eca809bSBen Skeggs 	if ((ret = calc_clk(clk, cstate, 0x00, nv_clk_src_gpc)) ||
3313eca809bSBen Skeggs 	    (ret = calc_clk(clk, cstate, 0x01, nv_clk_src_rop)) ||
3323eca809bSBen Skeggs 	    (ret = calc_clk(clk, cstate, 0x02, nv_clk_src_hubk07)) ||
3333eca809bSBen Skeggs 	    (ret = calc_clk(clk, cstate, 0x07, nv_clk_src_hubk06)) ||
3343eca809bSBen Skeggs 	    (ret = calc_clk(clk, cstate, 0x08, nv_clk_src_hubk01)) ||
3353eca809bSBen Skeggs 	    (ret = calc_clk(clk, cstate, 0x09, nv_clk_src_copy)) ||
336547dd271SBen Skeggs 	    (ret = calc_clk(clk, cstate, 0x0c, nv_clk_src_pmu)) ||
3373eca809bSBen Skeggs 	    (ret = calc_clk(clk, cstate, 0x0e, nv_clk_src_vdec)))
3387632b30eSBen Skeggs 		return ret;
3397632b30eSBen Skeggs 
3407632b30eSBen Skeggs 	return 0;
3417632b30eSBen Skeggs }
3427632b30eSBen Skeggs 
3437632b30eSBen Skeggs static void
gf100_clk_prog_0(struct gf100_clk * clk,int idx)3443eca809bSBen Skeggs gf100_clk_prog_0(struct gf100_clk *clk, int idx)
3457632b30eSBen Skeggs {
3463eca809bSBen Skeggs 	struct gf100_clk_info *info = &clk->eng[idx];
347822ad79fSBen Skeggs 	struct nvkm_device *device = clk->base.subdev.device;
3483eca809bSBen Skeggs 	if (idx < 7 && !info->ssel) {
349822ad79fSBen Skeggs 		nvkm_mask(device, 0x1371d0 + (idx * 0x04), 0x80003f3f, info->ddiv);
350822ad79fSBen Skeggs 		nvkm_wr32(device, 0x137160 + (idx * 0x04), info->dsrc);
3517632b30eSBen Skeggs 	}
3527632b30eSBen Skeggs }
3537632b30eSBen Skeggs 
3547632b30eSBen Skeggs static void
gf100_clk_prog_1(struct gf100_clk * clk,int idx)3553eca809bSBen Skeggs gf100_clk_prog_1(struct gf100_clk *clk, int idx)
3567632b30eSBen Skeggs {
357822ad79fSBen Skeggs 	struct nvkm_device *device = clk->base.subdev.device;
358822ad79fSBen Skeggs 	nvkm_mask(device, 0x137100, (1 << idx), 0x00000000);
3596979c630SBen Skeggs 	nvkm_msec(device, 2000,
3606979c630SBen Skeggs 		if (!(nvkm_rd32(device, 0x137100) & (1 << idx)))
3616979c630SBen Skeggs 			break;
3626979c630SBen Skeggs 	);
3637632b30eSBen Skeggs }
3647632b30eSBen Skeggs 
3657632b30eSBen Skeggs static void
gf100_clk_prog_2(struct gf100_clk * clk,int idx)3663eca809bSBen Skeggs gf100_clk_prog_2(struct gf100_clk *clk, int idx)
3677632b30eSBen Skeggs {
3683eca809bSBen Skeggs 	struct gf100_clk_info *info = &clk->eng[idx];
369822ad79fSBen Skeggs 	struct nvkm_device *device = clk->base.subdev.device;
3703eca809bSBen Skeggs 	const u32 addr = 0x137000 + (idx * 0x20);
3713eca809bSBen Skeggs 	if (idx <= 7) {
372822ad79fSBen Skeggs 		nvkm_mask(device, addr + 0x00, 0x00000004, 0x00000000);
373822ad79fSBen Skeggs 		nvkm_mask(device, addr + 0x00, 0x00000001, 0x00000000);
3747632b30eSBen Skeggs 		if (info->coef) {
375822ad79fSBen Skeggs 			nvkm_wr32(device, addr + 0x04, info->coef);
376822ad79fSBen Skeggs 			nvkm_mask(device, addr + 0x00, 0x00000001, 0x00000001);
3770f7fbb99SRoy Spliet 
3780f7fbb99SRoy Spliet 			/* Test PLL lock */
3790f7fbb99SRoy Spliet 			nvkm_mask(device, addr + 0x00, 0x00000010, 0x00000000);
3806979c630SBen Skeggs 			nvkm_msec(device, 2000,
3816979c630SBen Skeggs 				if (nvkm_rd32(device, addr + 0x00) & 0x00020000)
3826979c630SBen Skeggs 					break;
3836979c630SBen Skeggs 			);
3840f7fbb99SRoy Spliet 			nvkm_mask(device, addr + 0x00, 0x00000010, 0x00000010);
3850f7fbb99SRoy Spliet 
3860f7fbb99SRoy Spliet 			/* Enable sync mode */
3870f7fbb99SRoy Spliet 			nvkm_mask(device, addr + 0x00, 0x00000004, 0x00000004);
3887632b30eSBen Skeggs 		}
3897632b30eSBen Skeggs 	}
3907632b30eSBen Skeggs }
3917632b30eSBen Skeggs 
3927632b30eSBen Skeggs static void
gf100_clk_prog_3(struct gf100_clk * clk,int idx)3933eca809bSBen Skeggs gf100_clk_prog_3(struct gf100_clk *clk, int idx)
3947632b30eSBen Skeggs {
3953eca809bSBen Skeggs 	struct gf100_clk_info *info = &clk->eng[idx];
396822ad79fSBen Skeggs 	struct nvkm_device *device = clk->base.subdev.device;
3977632b30eSBen Skeggs 	if (info->ssel) {
398822ad79fSBen Skeggs 		nvkm_mask(device, 0x137100, (1 << idx), info->ssel);
3996979c630SBen Skeggs 		nvkm_msec(device, 2000,
4006979c630SBen Skeggs 			u32 tmp = nvkm_rd32(device, 0x137100) & (1 << idx);
4016979c630SBen Skeggs 			if (tmp == info->ssel)
4026979c630SBen Skeggs 				break;
4036979c630SBen Skeggs 		);
4047632b30eSBen Skeggs 	}
4057632b30eSBen Skeggs }
4067632b30eSBen Skeggs 
4077632b30eSBen Skeggs static void
gf100_clk_prog_4(struct gf100_clk * clk,int idx)4083eca809bSBen Skeggs gf100_clk_prog_4(struct gf100_clk *clk, int idx)
4097632b30eSBen Skeggs {
4103eca809bSBen Skeggs 	struct gf100_clk_info *info = &clk->eng[idx];
411822ad79fSBen Skeggs 	struct nvkm_device *device = clk->base.subdev.device;
412822ad79fSBen Skeggs 	nvkm_mask(device, 0x137250 + (idx * 0x04), 0x00003f3f, info->mdiv);
4137632b30eSBen Skeggs }
4147632b30eSBen Skeggs 
4157632b30eSBen Skeggs static int
gf100_clk_prog(struct nvkm_clk * base)4166625f55cSBen Skeggs gf100_clk_prog(struct nvkm_clk *base)
4177632b30eSBen Skeggs {
4186625f55cSBen Skeggs 	struct gf100_clk *clk = gf100_clk(base);
4197632b30eSBen Skeggs 	struct {
4203eca809bSBen Skeggs 		void (*exec)(struct gf100_clk *, int);
4217632b30eSBen Skeggs 	} stage[] = {
4227632b30eSBen Skeggs 		{ gf100_clk_prog_0 }, /* div programming */
4237632b30eSBen Skeggs 		{ gf100_clk_prog_1 }, /* select div mode */
4247632b30eSBen Skeggs 		{ gf100_clk_prog_2 }, /* (maybe) program pll */
4257632b30eSBen Skeggs 		{ gf100_clk_prog_3 }, /* (maybe) select pll mode */
4267632b30eSBen Skeggs 		{ gf100_clk_prog_4 }, /* final divider */
4277632b30eSBen Skeggs 	};
4287632b30eSBen Skeggs 	int i, j;
4297632b30eSBen Skeggs 
4307632b30eSBen Skeggs 	for (i = 0; i < ARRAY_SIZE(stage); i++) {
4313eca809bSBen Skeggs 		for (j = 0; j < ARRAY_SIZE(clk->eng); j++) {
4323eca809bSBen Skeggs 			if (!clk->eng[j].freq)
4337632b30eSBen Skeggs 				continue;
4343eca809bSBen Skeggs 			stage[i].exec(clk, j);
4357632b30eSBen Skeggs 		}
4367632b30eSBen Skeggs 	}
4377632b30eSBen Skeggs 
4387632b30eSBen Skeggs 	return 0;
4397632b30eSBen Skeggs }
4407632b30eSBen Skeggs 
4417632b30eSBen Skeggs static void
gf100_clk_tidy(struct nvkm_clk * base)4426625f55cSBen Skeggs gf100_clk_tidy(struct nvkm_clk *base)
4437632b30eSBen Skeggs {
4446625f55cSBen Skeggs 	struct gf100_clk *clk = gf100_clk(base);
4453eca809bSBen Skeggs 	memset(clk->eng, 0x00, sizeof(clk->eng));
4467632b30eSBen Skeggs }
4477632b30eSBen Skeggs 
4486625f55cSBen Skeggs static const struct nvkm_clk_func
4496625f55cSBen Skeggs gf100_clk = {
4506625f55cSBen Skeggs 	.read = gf100_clk_read,
4516625f55cSBen Skeggs 	.calc = gf100_clk_calc,
4526625f55cSBen Skeggs 	.prog = gf100_clk_prog,
4536625f55cSBen Skeggs 	.tidy = gf100_clk_tidy,
4546625f55cSBen Skeggs 	.domains = {
4557632b30eSBen Skeggs 		{ nv_clk_src_crystal, 0xff },
4567632b30eSBen Skeggs 		{ nv_clk_src_href   , 0xff },
4577632b30eSBen Skeggs 		{ nv_clk_src_hubk06 , 0x00 },
4587632b30eSBen Skeggs 		{ nv_clk_src_hubk01 , 0x01 },
4597632b30eSBen Skeggs 		{ nv_clk_src_copy   , 0x02 },
4604b9ce6e7SKarol Herbst 		{ nv_clk_src_gpc    , 0x03, NVKM_CLK_DOM_FLAG_VPSTATE, "core", 2000 },
4617632b30eSBen Skeggs 		{ nv_clk_src_rop    , 0x04 },
4627632b30eSBen Skeggs 		{ nv_clk_src_mem    , 0x05, 0, "memory", 1000 },
4637632b30eSBen Skeggs 		{ nv_clk_src_vdec   , 0x06 },
464547dd271SBen Skeggs 		{ nv_clk_src_pmu    , 0x0a },
4657632b30eSBen Skeggs 		{ nv_clk_src_hubk07 , 0x0b },
4667632b30eSBen Skeggs 		{ nv_clk_src_max }
4676625f55cSBen Skeggs 	}
4687632b30eSBen Skeggs };
4697632b30eSBen Skeggs 
4706625f55cSBen Skeggs int
gf100_clk_new(struct nvkm_device * device,enum nvkm_subdev_type type,int inst,struct nvkm_clk ** pclk)471*98fd7f83SBen Skeggs gf100_clk_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
472*98fd7f83SBen Skeggs 	      struct nvkm_clk **pclk)
4737632b30eSBen Skeggs {
4743eca809bSBen Skeggs 	struct gf100_clk *clk;
4757632b30eSBen Skeggs 
4766625f55cSBen Skeggs 	if (!(clk = kzalloc(sizeof(*clk), GFP_KERNEL)))
4776625f55cSBen Skeggs 		return -ENOMEM;
4786625f55cSBen Skeggs 	*pclk = &clk->base;
4797632b30eSBen Skeggs 
480*98fd7f83SBen Skeggs 	return nvkm_clk_ctor(&gf100_clk, device, type, inst, false, &clk->base);
4817632b30eSBen Skeggs }
482