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