drm/nva3/clk: HOST clock
authorRoy Spliet <rspliet@eclipso.eu>
Thu, 21 Aug 2014 11:45:14 +0000 (13:45 +0200)
committerBen Skeggs <bskeggs@redhat.com>
Mon, 15 Sep 2014 12:25:00 +0000 (22:25 +1000)
Signed-off-by: Roy Spliet <rspliet@eclipso.eu>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c
drivers/gpu/drm/nouveau/core/subdev/clock/nva3.h

index 3ad8d64ff6dcc62063818df7b3fa8070fd74f2f1..c676de6d1da61d03a0e4bc31d39761f37a950063 100644 (file)
@@ -136,12 +136,11 @@ static int
 nva3_clock_read(struct nouveau_clock *clk, enum nv_clk_src src)
 {
        struct nva3_clock_priv *priv = (void *)clk;
+       u32 hsrc;
 
        switch (src) {
        case nv_clk_src_crystal:
                return nv_device(priv)->crystal;
-       case nv_clk_src_href:
-               return 100000;
        case nv_clk_src_core:
                return read_pll(priv, 0x00, 0x4200);
        case nv_clk_src_shader:
@@ -154,6 +153,18 @@ nva3_clock_read(struct nouveau_clock *clk, enum nv_clk_src src)
                return read_clk(priv, 0x21, false);
        case nv_clk_src_daemon:
                return read_clk(priv, 0x25, false);
+       case nv_clk_src_host:
+               hsrc = (nv_rd32(priv, 0xc040) & 0x30000000) >> 28;
+               switch (hsrc) {
+               case 0:
+                       return read_clk(priv, 0x1d, false);
+               case 2:
+               case 3:
+                       return 277000;
+               default:
+                       nv_error(clk, "unknown HOST clock source %d\n", hsrc);
+                       return -EINVAL;
+               }
        default:
                nv_error(clk, "invalid clock source %d\n", src);
                return -EINVAL;
@@ -258,6 +269,34 @@ calc_clk(struct nva3_clock_priv *priv, struct nouveau_cstate *cstate,
        return ret;
 }
 
+static int
+calc_host(struct nva3_clock_priv *priv, struct nouveau_cstate *cstate)
+{
+       int ret = 0;
+       u32 kHz = cstate->domain[nv_clk_src_host];
+       struct nva3_clock_info *info = &priv->eng[nv_clk_src_host];
+
+       if (kHz == 277000) {
+               info->clk = 0;
+               info->host_out = NVA3_HOST_277;
+               return 0;
+       }
+
+       info->host_out = NVA3_HOST_CLK;
+
+       ret = nva3_clk_info(&priv->base, 0x1d, kHz, info);
+       if (ret >= 0)
+               return 0;
+       return ret;
+}
+
+static void
+disable_clk_src(struct nva3_clock_priv *priv, u32 src)
+{
+       nv_mask(priv, src, 0x00000100, 0x00000000);
+       nv_mask(priv, src, 0x00000001, 0x00000000);
+}
+
 static void
 prog_pll(struct nva3_clock_priv *priv, int clk, u32 pll, int idx)
 {
@@ -275,15 +314,13 @@ prog_pll(struct nva3_clock_priv *priv, int clk, u32 pll, int idx)
                nv_wait(priv, ctrl, 0x00020000, 0x00020000);
                nv_mask(priv, ctrl, 0x00000010, 0x00000010);
                nv_mask(priv, ctrl, 0x00000008, 0x00000000);
-               nv_mask(priv, src1, 0x00000100, 0x00000000);
-               nv_mask(priv, src1, 0x00000001, 0x00000000);
+               disable_clk_src(priv, src1);
        } else {
                nv_mask(priv, src1, 0x003f3141, 0x00000101 | info->clk);
                nv_mask(priv, ctrl, 0x00000018, 0x00000018);
                udelay(20);
                nv_mask(priv, ctrl, 0x00000001, 0x00000000);
-               nv_mask(priv, src0, 0x00000100, 0x00000000);
-               nv_mask(priv, src0, 0x00000001, 0x00000000);
+               disable_clk_src(priv, src0);
        }
 }
 
@@ -294,6 +331,33 @@ prog_clk(struct nva3_clock_priv *priv, int clk, int idx)
        nv_mask(priv, 0x004120 + (clk * 4), 0x003f3141, 0x00000101 | info->clk);
 }
 
+static void
+prog_host(struct nva3_clock_priv *priv)
+{
+       struct nva3_clock_info *info = &priv->eng[nv_clk_src_host];
+       u32 hsrc = (nv_rd32(priv, 0xc040));
+
+       switch (info->host_out) {
+       case NVA3_HOST_277:
+               if ((hsrc & 0x30000000) == 0) {
+                       nv_wr32(priv, 0xc040, hsrc | 0x20000000);
+                       disable_clk_src(priv, 0x4194);
+               }
+               break;
+       case NVA3_HOST_CLK:
+               prog_clk(priv, 0x1d, nv_clk_src_host);
+               if ((hsrc & 0x30000000) >= 0x20000000) {
+                       nv_wr32(priv, 0xc040, hsrc & ~0x30000000);
+               }
+               break;
+       default:
+               break;
+       }
+
+       /* This seems to be a clock gating factor on idle, always set to 64 */
+       nv_wr32(priv, 0xc044, 0x3e);
+}
+
 static int
 nva3_clock_calc(struct nouveau_clock *clk, struct nouveau_cstate *cstate)
 {
@@ -303,7 +367,8 @@ nva3_clock_calc(struct nouveau_clock *clk, struct nouveau_cstate *cstate)
        if ((ret = calc_clk(priv, cstate, 0x10, 0x4200, nv_clk_src_core)) ||
            (ret = calc_clk(priv, cstate, 0x11, 0x4220, nv_clk_src_shader)) ||
            (ret = calc_clk(priv, cstate, 0x20, 0x0000, nv_clk_src_disp)) ||
-           (ret = calc_clk(priv, cstate, 0x21, 0x0000, nv_clk_src_vdec)))
+           (ret = calc_clk(priv, cstate, 0x21, 0x0000, nv_clk_src_vdec)) ||
+           (ret = calc_host(priv, cstate)))
                return ret;
 
        return 0;
@@ -317,6 +382,7 @@ nva3_clock_prog(struct nouveau_clock *clk)
        prog_pll(priv, 0x01, 0x004220, nv_clk_src_shader);
        prog_clk(priv, 0x20, nv_clk_src_disp);
        prog_clk(priv, 0x21, nv_clk_src_vdec);
+       prog_host(priv);
        return 0;
 }
 
@@ -328,12 +394,12 @@ nva3_clock_tidy(struct nouveau_clock *clk)
 static struct nouveau_clocks
 nva3_domain[] = {
        { nv_clk_src_crystal, 0xff },
-       { nv_clk_src_href   , 0xff },
        { nv_clk_src_core   , 0x00, 0, "core", 1000 },
        { nv_clk_src_shader , 0x01, 0, "shader", 1000 },
        { nv_clk_src_mem    , 0x02, 0, "memory", 1000 },
        { nv_clk_src_vdec   , 0x03 },
        { nv_clk_src_disp   , 0x04 },
+       { nv_clk_src_host   , 0x05 },
        { nv_clk_src_max }
 };
 
index 2b4b3ead800d1a7defcc881fba488339470728d4..54f99491fb63814e00300ad4d5502ec5d15ee4b9 100644 (file)
@@ -6,6 +6,10 @@
 struct nva3_clock_info {
        u32 clk;
        u32 pll;
+       enum {
+               NVA3_HOST_277,
+               NVA3_HOST_CLK,
+       } host_out;
 };
 
 int nva3_pll_info(struct nouveau_clock *, int, u32, u32,