modClock.c revision 616f0718
1#include <unistd.h>
2#include <stdio.h>
3#include <stdlib.h>
4
5#ifndef Lynx
6#include <fnmatch.h>
7#endif
8
9#include "iopl.h"
10
11#define tolerance 0.01 /* +/- 1% */
12
13
14#define CT65520 0x1
15#define CT65525 0x2
16#define CT65530 0x3
17#define CT64200 0x4
18
19#define CT65535 0x11
20#define CT65540 0x12
21#define CT65545 0x13
22#define CT65546 0x14
23#define CT65548 0x15
24#define CT64300 0x16
25
26#define CT65550 0x31
27#define CT65554 0x32
28#define CT65555 0x33
29#define CT68554 0x34
30#define CT69000 0x35
31#define CT69030 0x36
32
33#define IS_Programmable(X) X&0x10
34#define IS_HiQV(X) X&0x20
35
36#define DotClk 0
37#define MemClk 1
38#define IS_MemClk(X) X&0x1
39
40int compute_clock (
41		   unsigned int ChipType,
42		   double target,
43		   double Fref,
44		   unsigned int ClkMaxN,
45		   unsigned int ClkMaxM,
46		   unsigned int *bestM,
47		   unsigned int *bestN,
48		   unsigned int *bestP,
49		   unsigned int *bestPSN) {
50
51  unsigned int M, N, P, PSN, PSNx;
52
53  double bestError = 0, abest = 42, bestFout = 0;
54
55  double Fvco, Fout;
56  double error, aerror;
57
58  unsigned int M_min = 3;
59  unsigned int M_max = ClkMaxM;
60
61  if (target < 1e6){
62    fprintf (stderr, "MHz assumed, changed to %g MHz\n", target);
63    target *= 1e6;
64  }
65
66  if (target > 220.0e6) {
67    fprintf (stderr, "too large\n");
68    return 1;
69  }
70
71  /* Other parameters available onthe 65548 but not the 65545, and
72     not documented in the Clock Synthesizer doc in rev 1.0 of the
73     65548 datasheet:
74
75     + XR30[4] = 0, VCO divider loop uses divide by 4 (same as 65545)
76		 1, VCO divider loop uses divide by 16
77
78     + XR30[5] = 1, reference clock is divided by 5
79
80     I haven't put in any support for those here.  For simplicity,
81     they should be set to 0 on the 65548, and left untouched on
82     earlier chips.  */
83
84  for (PSNx = ((ChipType == CT69000) || (ChipType == CT69030)) ? 1 : 0;
85       PSNx <= 1; PSNx++) {
86    unsigned int low_N, high_N;
87    double Fref4PSN;
88
89    PSN = PSNx ? 1 : 4;
90
91    low_N = 3;
92    high_N = ClkMaxN;
93
94    while (Fref / (PSN * low_N) > (((ChipType == CT69000) ||
95				    (ChipType == CT69030)) ? 5.0e6 : 2.0e6))
96      low_N++;
97    while (Fref / (PSN * high_N) < 150.0e3)
98      high_N--;
99
100    Fref4PSN = Fref * 4 / PSN;
101    for (N = low_N; N <= high_N; N++) {
102      double tmp = Fref4PSN / N;
103
104      for (P = (IS_HiQV(ChipType) && (ChipType != CT69000) &&
105		(ChipType != CT69030)) ? 1 : 0; P <= 5; P++) {
106	double Fvco_desired = target * (1 << P);
107	double M_desired = Fvco_desired / tmp;
108	/* Which way will M_desired be rounded?  Do all three just to
109	   be safe.  */
110	unsigned int M_low = M_desired - 1;
111	unsigned int M_hi = M_desired + 1;
112
113	if (M_hi < M_min || M_low > M_max)
114	  continue;
115
116	if (M_low < M_min)
117	  M_low = M_min;
118	if (M_hi > M_max)
119	  M_hi = M_max;
120
121	for (M = M_low; M <= M_hi; M++) {
122	  Fvco = tmp * M;
123	  if (Fvco <= ((ChipType == CT69000) || (ChipType == CT69030) ?
124		       100e6 : 48.0e6))
125	    continue;
126	  if (Fvco > 220.0e6)
127	    break;
128
129	  Fout = Fvco / (1 << P);
130
131	  error = (target - Fout) / target;
132
133	  aerror = (error < 0) ? -error : error;
134	  if (aerror < abest) {
135	    abest = aerror;
136	    bestError = error;
137	    *bestM = M;
138	    *bestN = N;
139	    *bestP = P;
140	    *bestPSN = PSN;
141	    bestFout = Fout;
142	  }
143	}
144      }
145    }
146  }
147
148  if (abest < tolerance) {
149    printf ("best: M=%d N=%d P=%d PSN=%d\n", *bestM, *bestN, *bestP, *bestPSN);
150
151    if (bestFout > 1.0e6)
152      printf ("Fout = %g MHz", bestFout / 1.0e6);
153    else if (bestFout > 1.0e3)
154      printf ("Fout = %g kHz", bestFout / 1.0e3);
155    else
156      printf ("Fout = %g Hz", bestFout);
157    printf (", error = %g\n", bestError);
158    return 0;
159  }
160  printf ("can't do it with less than %g error\n", bestError);
161  return 1;
162}
163
164int set_clock(
165	      unsigned int ChipType,
166	      unsigned int ClockType,
167	      unsigned int ProgClock,
168	      unsigned int M,
169	      unsigned int N,
170	      unsigned int P,
171	      unsigned int PSN) {
172
173  unsigned int tmp, idx;
174
175  SET_IOPL();
176
177  idx = inb(0x3D6);
178  if (IS_HiQV(ChipType)) {
179    if (IS_MemClk(ClockType)) {
180      printf ("XRCC = 0x%02X\n", M - 2);
181      printf ("XRCD = 0x%02X\n", N - 2);
182      printf ("XRCE = 0x%02X\n", (0x80 | (P * 16 + (PSN == 1))));
183
184      outb(0x3D6, 0xCE); /* Select Fix MClk before */
185      tmp = inb(0x3D7);
186      outb(0x3D7, tmp & 0x7F);
187      outb(0x3D6, 0xCC);
188      outb(0x3D7, (M - 2));
189      outb(0x3D6, 0xCD);
190      outb(0x3D7, (N - 2));
191      outb(0x3D6, 0xCE);
192      outb(0x3D7, (0x80 | (P * 16 + (PSN == 1))));
193    } else {
194      printf ("XR%X = 0x%02X\n", 0xC0 + 4 * ProgClock, M - 2);
195      printf ("XR%X = 0x%02X\n",  0xC1 + 4 * ProgClock, N - 2);
196      printf ("XR%X = 0x%02X\n",  0xC2 + 4 * ProgClock, 0);
197      printf ("XR%X = 0x%02X\n",  0xC3 + 4 * ProgClock, P * 16 + (PSN == 1));
198
199      outb(0x3D6, 0xC0 + 4 * ProgClock);
200      outb(0x3D7, (M - 2));
201      outb(0x3D6, 0xC1 + 4 * ProgClock);
202      outb(0x3D7, (N - 2));
203      outb(0x3D6, 0xC2 + 4 * ProgClock);
204      outb(0x3D7, 0x0);
205      outb(0x3D6, 0xC3 + 4 * ProgClock);
206      outb(0x3D7, (P * 16 + (PSN == 1)));
207    }
208   } else {
209    printf ("XR30 = 0x%02X\n", P * 2 + (PSN == 1));
210    printf ("XR31 = 0x%02X\n", M - 2);
211    printf ("XR32 = 0x%02X\n", N - 2);
212    outb(0x3D6, 0x33);
213    tmp = inb(0x3D7);
214    if (IS_MemClk(ClockType)) {
215      outb(0x3D7, tmp | 0x20);
216    } else {
217      outb(0x3D7, tmp & ~0x20);
218    }
219    outb(0x3D6, 0x30);
220    outb(0x3D7, (P * 2 + (PSN == 1)));
221    outb(0x3D6, 0x31);
222    outb(0x3D7, (M - 2));
223    outb(0x3D6, 0x32);
224    outb(0x3D7, (N - 2));
225    outb(0x3D6, 0x33);
226    outb(0x3D7, tmp);
227  }
228  outb(0x3D6, idx);
229  RESET_IOPL();
230  return 0;
231}
232
233unsigned int probe_chip(void) {
234
235  unsigned int ChipType, temp;
236
237  SET_IOPL();
238
239  outb(0x3D6, 0x00);
240  temp = inb(0x3D7);
241  ChipType = 0;
242  if (temp != 0xA5) {
243    if ((temp & 0xF0) == 0x70) {
244      ChipType = CT65520;
245    }
246    if ((temp & 0xF0) == 0x80) {  /* could also be a 65525 */
247      ChipType = CT65530;
248    }
249    if ((temp & 0xF0) == 0xA0) {
250      ChipType = CT64200;
251    }
252    if ((temp & 0xF0) == 0xB0) {
253      ChipType = CT64300;
254    }
255    if ((temp & 0xF0) == 0xC0) {
256      ChipType = CT65535;
257    }
258    if ((temp & 0xF8) == 0xD0) {
259      ChipType = CT65540;
260    }
261    if ((temp & 0xF8) == 0xD8) {
262      switch (temp & 0x07) {
263      case 3:
264	ChipType = CT65546;
265	break;
266      case 4:
267	ChipType = CT65548;
268	break;
269      default:
270	ChipType = CT65545;
271      }
272    }
273  }
274  /* At this point the chip could still be a HiQV, so check for
275   * that. This test needs some looking at */
276  if ((temp != 0) && (ChipType == 0)) {
277    outb(0x3D6, 0x02);
278    temp = inb(0x03D7);
279    if (temp == 0xE0) {
280      ChipType = CT65550;
281    }
282    if (temp == 0xE4) {
283      ChipType = CT65554;
284    }
285    if (temp == 0xE5) {
286      ChipType = CT65555;
287    }
288    if (temp == 0xF4) {
289      ChipType = CT68554;
290    }
291    if (temp == 0xC0) {
292      ChipType = CT69000;
293    }
294    if (temp == 0x30) {
295      outb(0x3D6, 0x03);
296      temp = inb(0x03D7);
297      if (temp == 0x0C) ChipType = CT69030;
298    }
299  }
300
301  RESET_IOPL();
302
303  if (ChipType == 0) {      /* failure */
304    fprintf(stderr, "Not a Chips and Technologies Chipset\n");
305  }
306
307  return ChipType;
308}
309
310
311int main (int argc, char *argv[]) {
312  double target;
313  double Fref = 14318180;
314  unsigned int M, N, P, PSN, ChipType, ClockType, progclock;
315
316  switch (argc) {
317  case 2:
318    progclock = 2;
319    target = atof (argv[1]);
320    break;
321  case 3:
322    progclock = abs(atof (argv[1]));
323    target = atof (argv[2]);
324    break;
325  default:
326    fprintf (stderr, "usage: %s [-0|-1|-2] freq\n", argv[0]);
327    return 1;
328  }
329
330  ClockType = DotClk;
331#ifndef Lynx
332  if (! fnmatch("*memClock",argv[0],FNM_PATHNAME)) {
333#else
334  if (strstr("memClock",argv[0]) != NULL) {
335#endif
336    ClockType = MemClk;
337  }
338
339  ChipType = probe_chip();
340  if (!ChipType) {
341    return 1;
342  }
343
344  if (!(IS_Programmable(ChipType))) {
345    fprintf(stderr, "No programmable Clock!\n");
346    return 1;
347  }
348
349  if (IS_HiQV(ChipType)) {
350    if (! compute_clock(ChipType, target, Fref, 63, 127, &M, &N, &P, &PSN)) {
351      return set_clock(ChipType, ClockType, progclock, M, N, P, PSN);
352    } else {
353      return 1;
354    }
355  } else {
356    if (! compute_clock(ChipType, target, Fref, 127, 127, &M, &N, &P, &PSN)) {
357      return set_clock(ChipType, ClockType, progclock, M, N, P, PSN);
358    } else {
359      return 1;
360    }
361  }
362}
363