You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
258 lines
6.5 KiB
258 lines
6.5 KiB
/*++
|
|
|
|
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;
|
|
}
|