1 | /* $NetBSD: nouveau_subdev_clock_nv40.c,v 1.1.1.1 2014/08/06 12:36:29 riastradh Exp $ */ |
2 | |
3 | /* |
4 | * Copyright 2012 Red Hat Inc. |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining a |
7 | * copy of this software and associated documentation files (the "Software"), |
8 | * to deal in the Software without restriction, including without limitation |
9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
10 | * and/or sell copies of the Software, and to permit persons to whom the |
11 | * Software is furnished to do so, subject to the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice shall be included in |
14 | * all copies or substantial portions of the Software. |
15 | * |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
19 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
20 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
21 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
22 | * OTHER DEALINGS IN THE SOFTWARE. |
23 | * |
24 | * Authors: Ben Skeggs |
25 | */ |
26 | |
27 | #include <sys/cdefs.h> |
28 | __KERNEL_RCSID(0, "$NetBSD: nouveau_subdev_clock_nv40.c,v 1.1.1.1 2014/08/06 12:36:29 riastradh Exp $" ); |
29 | |
30 | #include <subdev/clock.h> |
31 | #include <subdev/bios.h> |
32 | #include <subdev/bios/pll.h> |
33 | |
34 | #include "pll.h" |
35 | |
36 | struct nv40_clock_priv { |
37 | struct nouveau_clock base; |
38 | u32 ctrl; |
39 | u32 npll_ctrl; |
40 | u32 npll_coef; |
41 | u32 spll; |
42 | }; |
43 | |
44 | static struct nouveau_clocks |
45 | nv40_domain[] = { |
46 | { nv_clk_src_crystal, 0xff }, |
47 | { nv_clk_src_href , 0xff }, |
48 | { nv_clk_src_core , 0xff, 0, "core" , 1000 }, |
49 | { nv_clk_src_shader , 0xff, 0, "shader" , 1000 }, |
50 | { nv_clk_src_mem , 0xff, 0, "memory" , 1000 }, |
51 | { nv_clk_src_max } |
52 | }; |
53 | |
54 | static u32 |
55 | read_pll_1(struct nv40_clock_priv *priv, u32 reg) |
56 | { |
57 | u32 ctrl = nv_rd32(priv, reg + 0x00); |
58 | int P = (ctrl & 0x00070000) >> 16; |
59 | int N = (ctrl & 0x0000ff00) >> 8; |
60 | int M = (ctrl & 0x000000ff) >> 0; |
61 | u32 ref = 27000, clk = 0; |
62 | |
63 | if (ctrl & 0x80000000) |
64 | clk = ref * N / M; |
65 | |
66 | return clk >> P; |
67 | } |
68 | |
69 | static u32 |
70 | read_pll_2(struct nv40_clock_priv *priv, u32 reg) |
71 | { |
72 | u32 ctrl = nv_rd32(priv, reg + 0x00); |
73 | u32 coef = nv_rd32(priv, reg + 0x04); |
74 | int N2 = (coef & 0xff000000) >> 24; |
75 | int M2 = (coef & 0x00ff0000) >> 16; |
76 | int N1 = (coef & 0x0000ff00) >> 8; |
77 | int M1 = (coef & 0x000000ff) >> 0; |
78 | int P = (ctrl & 0x00070000) >> 16; |
79 | u32 ref = 27000, clk = 0; |
80 | |
81 | if ((ctrl & 0x80000000) && M1) { |
82 | clk = ref * N1 / M1; |
83 | if ((ctrl & 0x40000100) == 0x40000000) { |
84 | if (M2) |
85 | clk = clk * N2 / M2; |
86 | else |
87 | clk = 0; |
88 | } |
89 | } |
90 | |
91 | return clk >> P; |
92 | } |
93 | |
94 | static u32 |
95 | read_clk(struct nv40_clock_priv *priv, u32 src) |
96 | { |
97 | switch (src) { |
98 | case 3: |
99 | return read_pll_2(priv, 0x004000); |
100 | case 2: |
101 | return read_pll_1(priv, 0x004008); |
102 | default: |
103 | break; |
104 | } |
105 | |
106 | return 0; |
107 | } |
108 | |
109 | static int |
110 | nv40_clock_read(struct nouveau_clock *clk, enum nv_clk_src src) |
111 | { |
112 | struct nv40_clock_priv *priv = (void *)clk; |
113 | u32 mast = nv_rd32(priv, 0x00c040); |
114 | |
115 | switch (src) { |
116 | case nv_clk_src_crystal: |
117 | return nv_device(priv)->crystal; |
118 | case nv_clk_src_href: |
119 | return 100000; /*XXX: PCIE/AGP differ*/ |
120 | case nv_clk_src_core: |
121 | return read_clk(priv, (mast & 0x00000003) >> 0); |
122 | case nv_clk_src_shader: |
123 | return read_clk(priv, (mast & 0x00000030) >> 4); |
124 | case nv_clk_src_mem: |
125 | return read_pll_2(priv, 0x4020); |
126 | default: |
127 | break; |
128 | } |
129 | |
130 | nv_debug(priv, "unknown clock source %d 0x%08x\n" , src, mast); |
131 | return -EINVAL; |
132 | } |
133 | |
134 | static int |
135 | nv40_clock_calc_pll(struct nv40_clock_priv *priv, u32 reg, u32 clk, |
136 | int *N1, int *M1, int *N2, int *M2, int *log2P) |
137 | { |
138 | struct nouveau_bios *bios = nouveau_bios(priv); |
139 | struct nvbios_pll pll; |
140 | int ret; |
141 | |
142 | ret = nvbios_pll_parse(bios, reg, &pll); |
143 | if (ret) |
144 | return ret; |
145 | |
146 | if (clk < pll.vco1.max_freq) |
147 | pll.vco2.max_freq = 0; |
148 | |
149 | ret = nv04_pll_calc(nv_subdev(priv), &pll, clk, N1, M1, N2, M2, log2P); |
150 | if (ret == 0) |
151 | return -ERANGE; |
152 | return ret; |
153 | } |
154 | |
155 | static int |
156 | nv40_clock_calc(struct nouveau_clock *clk, struct nouveau_cstate *cstate) |
157 | { |
158 | struct nv40_clock_priv *priv = (void *)clk; |
159 | int gclk = cstate->domain[nv_clk_src_core]; |
160 | int sclk = cstate->domain[nv_clk_src_shader]; |
161 | int N1, M1, N2, M2, log2P; |
162 | int ret; |
163 | |
164 | /* core/geometric clock */ |
165 | ret = nv40_clock_calc_pll(priv, 0x004000, gclk, |
166 | &N1, &M1, &N2, &M2, &log2P); |
167 | if (ret < 0) |
168 | return ret; |
169 | |
170 | if (N2 == M2) { |
171 | priv->npll_ctrl = 0x80000100 | (log2P << 16); |
172 | priv->npll_coef = (N1 << 8) | M1; |
173 | } else { |
174 | priv->npll_ctrl = 0xc0000000 | (log2P << 16); |
175 | priv->npll_coef = (N2 << 24) | (M2 << 16) | (N1 << 8) | M1; |
176 | } |
177 | |
178 | /* use the second pll for shader/rop clock, if it differs from core */ |
179 | if (sclk && sclk != gclk) { |
180 | ret = nv40_clock_calc_pll(priv, 0x004008, sclk, |
181 | &N1, &M1, NULL, NULL, &log2P); |
182 | if (ret < 0) |
183 | return ret; |
184 | |
185 | priv->spll = 0xc0000000 | (log2P << 16) | (N1 << 8) | M1; |
186 | priv->ctrl = 0x00000223; |
187 | } else { |
188 | priv->spll = 0x00000000; |
189 | priv->ctrl = 0x00000333; |
190 | } |
191 | |
192 | return 0; |
193 | } |
194 | |
195 | static int |
196 | nv40_clock_prog(struct nouveau_clock *clk) |
197 | { |
198 | struct nv40_clock_priv *priv = (void *)clk; |
199 | nv_mask(priv, 0x00c040, 0x00000333, 0x00000000); |
200 | nv_wr32(priv, 0x004004, priv->npll_coef); |
201 | nv_mask(priv, 0x004000, 0xc0070100, priv->npll_ctrl); |
202 | nv_mask(priv, 0x004008, 0xc007ffff, priv->spll); |
203 | mdelay(5); |
204 | nv_mask(priv, 0x00c040, 0x00000333, priv->ctrl); |
205 | return 0; |
206 | } |
207 | |
208 | static void |
209 | nv40_clock_tidy(struct nouveau_clock *clk) |
210 | { |
211 | } |
212 | |
213 | static int |
214 | nv40_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine, |
215 | struct nouveau_oclass *oclass, void *data, u32 size, |
216 | struct nouveau_object **pobject) |
217 | { |
218 | struct nv40_clock_priv *priv; |
219 | int ret; |
220 | |
221 | ret = nouveau_clock_create(parent, engine, oclass, nv40_domain, &priv); |
222 | *pobject = nv_object(priv); |
223 | if (ret) |
224 | return ret; |
225 | |
226 | priv->base.pll_calc = nv04_clock_pll_calc; |
227 | priv->base.pll_prog = nv04_clock_pll_prog; |
228 | priv->base.read = nv40_clock_read; |
229 | priv->base.calc = nv40_clock_calc; |
230 | priv->base.prog = nv40_clock_prog; |
231 | priv->base.tidy = nv40_clock_tidy; |
232 | return 0; |
233 | } |
234 | |
235 | struct nouveau_oclass |
236 | nv40_clock_oclass = { |
237 | .handle = NV_SUBDEV(CLOCK, 0x40), |
238 | .ofuncs = &(struct nouveau_ofuncs) { |
239 | .ctor = nv40_clock_ctor, |
240 | .dtor = _nouveau_clock_dtor, |
241 | .init = _nouveau_clock_init, |
242 | .fini = _nouveau_clock_fini, |
243 | }, |
244 | }; |
245 | |