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