/*++ Copyright (c) 1990-1995 Microsoft Corporation Module Name: nnclk.c Abstract: This module contains the code to set the number nine clock. Environment: Kernel mode Revision History: --*/ #include "s3.h" #if defined(ALLOC_PRAGMA) #pragma alloc_text(PAGE, calc_clock) #pragma alloc_text(PAGE, gcd) #pragma alloc_text(PAGE, set_clock) #endif #define PROM_WRITE_INDEX 0x51 #define PROM_WRITE_BIT 0x80 #define SSW_READ_ENBL_INDEX 0x55 #define SSW_READ_ENBL_BIT 0x04 #define SSW_READ_PORT 0x03C8 #define SSW_WRITE_INDEX 0x5C #define LOCK_INDEX 0x39 #define UNLOCK_PATTERN 0xA0 #define LOCK_INDEX2 0x38 #define UNLOCK_PATTERN2 0x48 #define BIOS_32K_INDEX 0x31 #define BIOS_32K_BIT 0x80 #define MODE_CTRL_INDEX 0x42 #define GOPA_FLSEL 0x40 #define GOPB_ENABLE 0x80 #define GOPB_SLED 0x40 #define GOPB_FLSEL 0x20 #define GOPB_BURN 0x10 #undef MIN #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #undef MAX #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #define CRYSTAL_FREQUENCY (14318180 * 2) #define MIN_VCO_FREQUENCY 50000000 #define MAX_NUMERATOR 130 #define MAX_DENOMINATOR MIN(129, CRYSTAL_FREQUENCY / 400000) #define MIN_DENOMINATOR MAX(3, CRYSTAL_FREQUENCY / 2000000) /* Set up the softswitch write value */ #define CLOCK(x) VideoPortWritePortUchar(CRT_DATA_REG, (UCHAR)(iotemp | (x))) #define C_DATA 2 #define C_CLK 1 #define C_BOTH 3 #define C_NONE 0 /**************************************************************************** * calc_clock * * Usage: clock frequency [set] * frequency is specified in MHz * ***************************************************************************/ long calc_clock(frequency, select) register long frequency; /* in Hz */ int select; { register long index; long temp; long min_m, min_n, min_diff; long diff; int clock_m; int clock_n; int clock_p; min_diff = 0xFFFFFFF; min_n = 1; min_m = 1; /* Calculate 18 bit clock value */ clock_p = 0; if (frequency < MIN_VCO_FREQUENCY) clock_p = 1; if (frequency < MIN_VCO_FREQUENCY / 2) clock_p = 2; if (frequency < MIN_VCO_FREQUENCY / 4) clock_p = 3; frequency <<= clock_p; for (clock_n = 4; clock_n <= MAX_NUMERATOR; clock_n++) { index = CRYSTAL_FREQUENCY / (frequency / clock_n); if (index > MAX_DENOMINATOR) index = MAX_DENOMINATOR; if (index < MIN_DENOMINATOR) index = MIN_DENOMINATOR; for (clock_m = index - 3; clock_m < index + 4; clock_m++) if (clock_m >= MIN_DENOMINATOR && clock_m <= MAX_DENOMINATOR) { diff = (CRYSTAL_FREQUENCY / clock_m) * clock_n - frequency; if (diff < 0) diff = -diff; if (min_m * gcd(clock_m, clock_n) / gcd(min_m, min_n) == clock_m && min_n * gcd(clock_m, clock_n) / gcd(min_m, min_n) == clock_n) if (diff > min_diff) diff = min_diff; if (diff <= min_diff) { min_diff = diff; min_m = clock_m; min_n = clock_n; } } } clock_m = min_m; clock_n = min_n; /* Calculate the index */ temp = (((CRYSTAL_FREQUENCY / 2) * clock_n) / clock_m) << 1; for (index = 0; vclk_range[index + 1] < temp && index < 15; index++) ; /* Pack the clock value for the frequency snthesizer */ temp = (((long)clock_n - 3) << 11) + ((clock_m - 2) << 1) + (clock_p << 8) + (index << 18) + ((long)select << 22); return temp; } /****************************************************************************** * *****************************************************************************/ VOID set_clock( PHW_DEVICE_EXTENSION HwDeviceExtension, LONG clock_value) /* 7bits M, 7bits N, 2bits P */ { register long index; register char iotemp; int select; select = (clock_value >> 22) & 3; /* Unlock the S3 registers */ VideoPortWritePortUchar(CRT_ADDRESS_REG, LOCK_INDEX); VideoPortWritePortUchar(CRT_DATA_REG, UNLOCK_PATTERN); /* Shut off screen */ VideoPortWritePortUchar(SEQ_ADDRESS_REG, 0x01); iotemp = VideoPortReadPortUchar(SEQ_DATA_REG); VideoPortWritePortUchar(SEQ_DATA_REG, (UCHAR)(iotemp | 0x20)); /* set clock input to 11 binary */ iotemp = VideoPortReadPortUchar(MISC_OUTPUT_REG_READ); VideoPortWritePortUchar(MISC_OUTPUT_REG_WRITE, (UCHAR)(iotemp | 0x0C)); VideoPortWritePortUchar(CRT_ADDRESS_REG, SSW_WRITE_INDEX); VideoPortWritePortUchar(CRT_DATA_REG, 0); VideoPortWritePortUchar(CRT_ADDRESS_REG, MODE_CTRL_INDEX); iotemp = VideoPortReadPortUchar(CRT_DATA_REG) & 0xF0; /* Program the IC Designs 2061A frequency generator */ CLOCK(C_NONE); /* Unlock sequence */ CLOCK(C_DATA); for (index = 0; index < 6; index++) { CLOCK(C_BOTH); CLOCK(C_DATA); } CLOCK(C_NONE); CLOCK(C_CLK); CLOCK(C_NONE); CLOCK(C_CLK); /* Program the 24 bit value into REG0 */ for (index = 0; index < 24; index++) { /* Clock in the next bit */ clock_value >>= 1; if (clock_value & 1) { CLOCK(C_CLK); CLOCK(C_NONE); CLOCK(C_DATA); CLOCK(C_BOTH); } else { CLOCK(C_BOTH); CLOCK(C_DATA); CLOCK(C_NONE); CLOCK(C_CLK); } } CLOCK(C_BOTH); CLOCK(C_DATA); CLOCK(C_BOTH); /* If necessary, reprogram other ICD2061A registers to defaults */ /* Select the CLOCK in the frequency synthesizer */ CLOCK(C_NONE | select); /* Turn screen back on */ VideoPortWritePortUchar(SEQ_ADDRESS_REG, 0x01); iotemp = VideoPortReadPortUchar(SEQ_DATA_REG); VideoPortWritePortUchar(SEQ_DATA_REG, (UCHAR) (iotemp & 0xDF)); } /****************************************************************************** * Number theoretic function - GCD (Greatest Common Divisor) *****************************************************************************/ long gcd(a, b) register long a, b; { register long c = a % b; while (c) a = b, b = c, c = a % b; return b; }