blob: 3c73dcc9eac2c18be9b197ffed70697f4f027ead [file] [log] [blame]
Arthur Heymans73ea0322018-03-28 17:17:07 +02001--
2-- Copyright (C) 2016 secunet Security Networks AG
Nico Huberb47a5c42019-09-29 00:07:21 +02003-- Copyright (C) 2019 Nico Huber <nico.h@gmx.de>
Arthur Heymans73ea0322018-03-28 17:17:07 +02004--
5-- This program is free software; you can redistribute it and/or modify
6-- it under the terms of the GNU General Public License as published by
7-- the Free Software Foundation; either version 2 of the License, or
8-- (at your option) any later version.
9--
10-- This program is distributed in the hope that it will be useful,
11-- but WITHOUT ANY WARRANTY; without even the implied warranty of
12-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13-- GNU General Public License for more details.
14--
15
16with HW.Time;
17with HW.GFX.GMA.Config;
18with HW.GFX.GMA.Registers;
19
20package body HW.GFX.GMA.Power_And_Clocks is
21
22 FSB_FREQ_SEL_MASK : constant := 7 * 2 ** 0;
23 CLKCFG_FSB_400 : constant Frequency_Type := 100_000_000;
24 CLKCFG_FSB_533 : constant Frequency_Type := 133_333_333;
25 CLKCFG_FSB_667 : constant Frequency_Type := 166_666_666;
26 CLKCFG_FSB_800 : constant Frequency_Type := 200_000_000;
27 CLKCFG_FSB_1067 : constant Frequency_Type := 266_666_666;
28 CLKCFG_FSB_1333 : constant Frequency_Type := 333_333_333;
29
Nico Huberb47a5c42019-09-29 00:07:21 +020030 type Div_Array is array (0 .. 7) of Pos64;
31
32 procedure Get_VCO (VCO : out Int64; Divisors : out Div_Array)
33 is
34 G45_3200 : constant Div_Array := (12, 10, 8, 7, 5, 16, others => 1);
35 G45_4000 : constant Div_Array := (14, 12, 10, 8, 6, 20, others => 1);
36 G45_4800 : constant Div_Array := (20, 14, 12, 10, 8, 24, others => 1);
37 G45_5333 : constant Div_Array := (20, 16, 12, 12, 8, 28, others => 1);
38 G45_Divs : constant array (Natural range 0 .. 7) of Div_Array :=
39 (G45_3200, G45_4000, G45_5333, G45_4800, others => (others => 1));
40
41 GM45_2667 : constant Div_Array := (12, 8, others => 1);
42 GM45_3200 : constant Div_Array := (14, 10, others => 1);
43 GM45_4000 : constant Div_Array := (18, 12, others => 1);
44 GM45_5333 : constant Div_Array := (24, 16, others => 1);
45 GM45_Divs : constant array (Natural range 0 .. 7) of Div_Array :=
46 (0 => GM45_3200, 1 => GM45_4000, 2 => GM45_5333, 4 => GM45_2667,
47 others => (others => 1));
48
Arthur Heymans3f37cce2026-03-03 18:52:12 +010049 -- Crestline (GM965) VCO divisor tables from Linux' i965gm_get_cdclk
50 CL_3200 : constant Div_Array := (16, 10, 8, others => 1);
51 CL_4000 : constant Div_Array := (20, 12, 10, others => 1);
52 CL_5333 : constant Div_Array := (24, 16, 14, others => 1);
53 CL_Divs : constant array (Natural range 0 .. 7) of Div_Array :=
54 (0 => CL_3200, 1 => CL_4000, 2 => CL_5333,
55 others => (others => 1));
56
Nico Huberb47a5c42019-09-29 00:07:21 +020057 HPLLVCO : Word32;
58 VCO_Sel : Natural range 0 .. 7;
59 begin
Arthur Heymans3f37cce2026-03-03 18:52:12 +010060 if Config.GMCH_GM965 then
61 Registers.Read (Registers.GMCH_HPLLVCO_MOBILE, HPLLVCO);
62 VCO_Sel := Natural (HPLLVCO and 7);
63 VCO :=
64 (case VCO_Sel is
65 when 0 => 3_200_000_000,
66 when 1 => 4_000_000_000,
67 when 2 => 5_333_333_333,
68 when 3 => 6_400_000_000,
69 when 4 => 3_333_333_333,
70 when 5 => 3_566_666_667,
71 when 6 => 4_266_666_667,
72 when others => 0);
73 Divisors := CL_Divs (VCO_Sel);
74 elsif Config.Has_GMCH_Mobile_VCO then
Nico Huberb47a5c42019-09-29 00:07:21 +020075 Registers.Read (Registers.GMCH_HPLLVCO_MOBILE, HPLLVCO);
76 VCO_Sel := Natural (HPLLVCO and 7);
77 VCO :=
78 (case VCO_Sel is
79 when 0 => 3_200_000_000,
80 when 1 => 4_000_000_000,
81 when 2 => 5_333_333_333,
82 --when 3 => 6_400_000_000,
83 when 4 => 2_666_666_667,
84 --when 5 => 4_266_666_667,
85 when others => 0);
86 Divisors := GM45_Divs (VCO_Sel);
87 else
88 Registers.Read (Registers.GMCH_HPLLVCO, HPLLVCO);
89 VCO_Sel := Natural (HPLLVCO and 7);
90 VCO :=
91 (case VCO_Sel is
92 when 0 => 3_200_000_000,
93 when 1 => 4_000_000_000,
94 when 2 => 5_333_333_333,
95 when 3 => 4_800_000_000,
96 when others => 0);
97 Divisors := G45_Divs (VCO_Sel);
98 end if;
99 end Get_VCO;
100
101 procedure Get_CDClk (CDClk : out Config.CDClk_Range)
102 is
103 use type HW.Word16;
104
Nico Huber4ba49112020-06-10 16:31:36 +0200105 Tmp_Clk : Int64;
Nico Huberb47a5c42019-09-29 00:07:21 +0200106
107 VCO : Int64;
108 Divisors : Div_Array;
109
110 GCFGC : Word16;
111 CDClk_Sel : Natural range 0 .. 7;
112 begin
Nico Huber4ba49112020-06-10 16:31:36 +0200113 Get_VCO (VCO, Divisors);
114 PCI_Read16 (GCFGC, 16#f0#);
115 if Config.GMCH_GM965 then
116 -- Linux i965gm_get_cdclk: cdclk_sel = ((tmp >> 8) & 0x1f) - 1
117 if (Shift_Right (GCFGC, 8) and 16#1f#) in 1 .. 3 then
118 CDClk_Sel := Natural (Shift_Right (GCFGC, 8) and 16#1f#) - 1;
Nico Huberb47a5c42019-09-29 00:07:21 +0200119 else
Nico Huber4ba49112020-06-10 16:31:36 +0200120 CDClk_Sel := Div_Array'Last;
Nico Huberb47a5c42019-09-29 00:07:21 +0200121 end if;
Nico Huber4ba49112020-06-10 16:31:36 +0200122 elsif Config.Has_GMCH_Mobile_VCO then
123 CDClk_Sel := Natural (Shift_Right (GCFGC, 12) and 1);
124 else
125 CDClk_Sel := Natural (Shift_Right (GCFGC, 4) and 7);
Nico Huberb47a5c42019-09-29 00:07:21 +0200126 end if;
Nico Huber4ba49112020-06-10 16:31:36 +0200127 Tmp_Clk := VCO / Divisors (CDClk_Sel);
Nico Huberb47a5c42019-09-29 00:07:21 +0200128
129 if Tmp_Clk in Config.CDClk_Range then
130 CDClk := Tmp_Clk;
131 else
Arthur Heymans3f37cce2026-03-03 18:52:12 +0100132 if Config.GMCH_GM965 then
133 CDClk := 4_000_000_000 / 20; -- 200 MHz
134 elsif Config.Has_GMCH_Mobile_VCO then
Nico Huberb47a5c42019-09-29 00:07:21 +0200135 CDClk := 5_333_333_333 / 24;
136 else
137 CDClk := 5_333_333_333 / 28;
138 end if;
139 end if;
140 end Get_CDClk;
141
Arthur Heymans73ea0322018-03-28 17:17:07 +0200142 -- The Raw Freq is 1/4 of the FSB freq
Nico Huberb47a5c42019-09-29 00:07:21 +0200143 procedure Get_Raw_Clock (Raw_Clock : out Frequency_Type)
Arthur Heymans73ea0322018-03-28 17:17:07 +0200144 is
145 CLK_CFG : Word32;
146 type Freq_Sel is new Natural range 0 .. 7;
147 begin
148 Registers.Read
149 (Register => Registers.GMCH_CLKCFG,
150 Value => CLK_CFG);
151 case Freq_Sel (CLK_CFG and FSB_FREQ_SEL_MASK) is
Nico Huberb47a5c42019-09-29 00:07:21 +0200152 when 0 => Raw_Clock := CLKCFG_FSB_1067;
153 when 1 => Raw_Clock := CLKCFG_FSB_533;
154 when 2 => Raw_Clock := CLKCFG_FSB_800;
155 when 3 => Raw_Clock := CLKCFG_FSB_667;
156 when 4 => Raw_Clock := CLKCFG_FSB_1333;
157 when 5 => Raw_Clock := CLKCFG_FSB_400;
158 when 6 => Raw_Clock := CLKCFG_FSB_1067;
159 when 7 => Raw_Clock := CLKCFG_FSB_1333;
Arthur Heymans73ea0322018-03-28 17:17:07 +0200160 end case;
Nico Huberb47a5c42019-09-29 00:07:21 +0200161 end Get_Raw_Clock;
162
163 procedure Initialize
164 is
165 CDClk : Config.CDClk_Range;
166 begin
167 Get_CDClk (CDClk);
168 Config.CDClk := CDClk;
169 Config.Max_CDClk := CDClk;
170
171 Get_Raw_Clock (Config.Raw_Clock);
Arthur Heymans73ea0322018-03-28 17:17:07 +0200172 end Initialize;
173
Nico Huberb47a5c42019-09-29 00:07:21 +0200174 procedure Limit_Dotclocks
175 (Configs : in out Pipe_Configs;
176 CDClk_Switch : out Boolean)
177 is
178 begin
179 Config_Helpers.Limit_Dotclocks (Configs, Config.CDClk * 90 / 100);
180 CDClk_Switch := False;
181 end Limit_Dotclocks;
182
Nico Huber41e86742024-07-17 17:10:28 +0200183 procedure Power_Up (Port : Active_Port_Type; Success : out Boolean) is
184 begin
185 Success := True;
186 end Power_Up;
187
Arthur Heymans73ea0322018-03-28 17:17:07 +0200188end HW.GFX.GMA.Power_And_Clocks;