Ronald G. Minnich wrote:
I would like to see that code, thanks
ron
The original message is below since I think it did not post to the list due to my return email address (fixed that I think). The code is attached (6 files).
------
I think you have to cramp your C coding style anyway, to stay within registers. The extra scratch area does not help much, with chips such as the SiS630, only has three gp regs, little help. You can't go around declaring variables willy nilly, you run out of space (registers) no matter what compiler.
I also have been experimenting with inline gcc, and I think it works pretty well and once you get the hang of it, and it _is_ easier than assy. I have re-coded the console routines, and ram setup, and spd timing setup on the sis630 for C. It is still a wip but I have tested the spd setup by wrapping a real main on a live machine.
So far it is about 400 lines of C, should I attach it? It compiles without using the stack (except for a %ebp push/pop which can be deleted).
I can't see how a special compiler gets you enough more than gcc to be worth the downside of effort and debugging. Scratch regs vary from chip to chip, and use 1.5 regs to access (pci). You could use the %ebp and %esp which is a gain of two (and maybe %es %fs etc), but I think using gcc will get us there anyway.
Just another opinion to put in the hat.
-Steve
// // by S. Gehlbach <steve @ kesa . com> // // edit with tab stops =4 // // for SiS630 spd interface // #ifndef COMMON #include "common.h" #endif
#define SMA_SIZE 0x80 #define ACPI_BASE_ADDR 0x5000 #define SMB_BASE_ADDR ACPI_BASE_ADDR
/* Define register offsets */ #define SMB_STS 0x80 #define SMB_ENB 0x81 #define SMB_CNT 0x82 #define SMBHOST_CNT 0x83 #define SMB_ADDR 0x84 #define SMB_CMD 0x85 #define SMB_PCOUNT 0x86 #define SMB_COUNT 0x87 #define SMB_DATA 0x88
/* Define register settings */ #define SMB_KILL 0x20 #define DIMM_BASE 0xa1 // 1010001 is base for DIMM in SMBus #define READ_CMD 0x12 // start + R/W in CMD reg
/* Define SPD Data locations */ #define MEM_TYPE 2 // Memory Type - EDO, FPM, SDRAM #define NUM_ROWS 3 // Number of Row Addresses #define NUM_COLS 4 // Number of Column Addresses #define NUM_MOD_ROWS 5 // Number of Module Rows #define NUM_BANKS 17 // number of banks #define CAS_LAT 18 // CAS Latencies Supported #define MOD_ATTR 21 // SDRAM Module Attributes #define BANK_DENSITY 31 // Module Bank Density #define SPD_REV 62 // SPD Revision #define SPEC_FREQ 126 // Specification Frequency - 66, 100, ...
// one bank const char sdram_type_bank_1 [] = { // Column Number 8 9 10 11 Row Number 0x00, 0x04, 0x08, 0xff, // 11 0xff, 0xff, 0xff, 0xff, // 12 0x01, 0x05, 0x09, 0x0d // 13 };
// two banks const char sdram_type_bank_2 [] = { // Column Number 8 9 10 11 Row Number 0xc0, 0xff, 0xff, 0xff, // 11 0x02, 0x06, 0x0a, 0x0e, // 12 0x03, 0x07, 0x0b, 0x0f // 13 }; extern inline unsigned char read_spd(unsigned char byte_number) { outb(0xff,SMB_BASE_ADDR + SMB_STS); // clear status outb(byte_number, SMB_BASE_ADDR + SMB_CMD); // SMBus command outb(READ_CMD, SMB_BASE_ADDR + SMBHOST_CNT); // SMBus Host Control, Start, R/W while ( !(0x0a & inb(SMB_BASE_ADDR + SMB_STS)) ) ; // loop on status for error or done return inb(SMB_BASE_ADDR + SMB_DATA); }
extern inline set_ram (unsigned char slot) { // issue smbus kill command outb(SMB_KILL, SMB_BASE_ADDR + SMBHOST_CNT); // SMBus host control outb(0xff,SMB_BASE_ADDR + SMB_STS); // clear status
// address the bus // SPD is SMBus address 1010 xxxR where R=1 for read and xxx is dimm slot number outb(0xa1 + 2*slot,SMB_BASE_ADDR + SMB_ADDR); // SMBus Address
// read ram type (SPD byte 2) // skip if not SDRAM (type=4) if ( read_spd(MEM_TYPE) != 0x04 ) return;
// above should fail on non-ex simm but just to be sure... // check the status if (inb(SMB_BASE_ADDR + SMB_STS) != 0x08) return;
// need to ck and see if 0xff switch (read_spd(NUM_ROWS)) { case 11: case 12: case 13: break; default: // not in range, return return; } switch (read_spd(NUM_COLS)) { case 8: case 9: case 10: case 11: break; default: // not in range, return return; }
// // note we read the spd more than once but we are using it as our scratch area // since we have no scratch space just registers. // some table values are out of range but we will check them later and not enable ram. // switch (read_spd(NUM_BANKS)) {
case 1: pci_conf1_write_config_byte( sdram_type_bank_1[read_spd(NUM_ROWS)*4 + read_spd(NUM_COLS) - 52], CONFIG_CMD(NORTH_BRIDGE_BUS,PCI_DEVFN(NORTH_BRIDGE_DEV,NORTH_BRIDGE_FUN),0x60+slot) ); break; default: pci_conf1_write_config_byte( sdram_type_bank_2[read_spd(NUM_ROWS)*4 + read_spd(NUM_COLS) - 52], CONFIG_CMD(NORTH_BRIDGE_BUS,PCI_DEVFN(NORTH_BRIDGE_DEV,NORTH_BRIDGE_FUN),0x60+slot) ); }
// // check illegal values // return on error does not enable dimm so effectively it is disabled // switch (pci_conf1_read_config_byte(CONFIG_CMD(NORTH_BRIDGE_BUS,PCI_DEVFN(NORTH_BRIDGE_DEV,NORTH_BRIDGE_FUN),0x60+slot)) & 0x80) { case 0x80: return; default: }
// set module rows switch (read_spd(NUM_MOD_ROWS)) { case 2: pci_conf1_write_config_byte( 1<<5 | pci_conf1_read_config_byte(CONFIG_CMD(NORTH_BRIDGE_BUS,PCI_DEVFN(NORTH_BRIDGE_DEV,NORTH_BRIDGE_FUN),0x60+slot)), CONFIG_CMD(NORTH_BRIDGE_BUS,PCI_DEVFN(NORTH_BRIDGE_DEV,NORTH_BRIDGE_FUN),0x60+slot) ); } // // everything okay so set the enable bit // pci_conf1_write_config_byte( (1<<slot) | pci_conf1_read_config_byte(CONFIG_CMD(NORTH_BRIDGE_BUS,PCI_DEVFN(NORTH_BRIDGE_DEV,NORTH_BRIDGE_FUN),0x63)), CONFIG_CMD(NORTH_BRIDGE_BUS,PCI_DEVFN(NORTH_BRIDGE_DEV,NORTH_BRIDGE_FUN),0x63) ); }
extern inline void setup_chip () { // ACPI Enable pci_conf1_write_config_byte( 0x8a, CONFIG_CMD(LPC_BRIDGE_BUS,PCI_DEVFN(LPC_BRIDGE_DEV,LPC_BRIDGE_FUN),0x40) ); // Set ACPI Base I/O Address pci_conf1_write_config_byte( ACPI_BASE_ADDR>>16, CONFIG_CMD(LPC_BRIDGE_BUS,PCI_DEVFN(LPC_BRIDGE_DEV,LPC_BRIDGE_FUN),0x75) ); // MDOE# enable, this bit should be set before sizing pci_conf1_write_config_byte( 0x01, CONFIG_CMD(LPC_BRIDGE_BUS,PCI_DEVFN(LPC_BRIDGE_DEV,LPC_BRIDGE_FUN),0x55) ); // initialize the dimm register pci_conf1_write_config_byte(SMA_SIZE, CONFIG_CMD(NORTH_BRIDGE_BUS,PCI_DEVFN(NORTH_BRIDGE_DEV,NORTH_BRIDGE_FUN),0x63)); // check each dimm slot set_ram(0); set_ram(1); set_ram(2); }
// // by S. Gehlbach // edit with :set ts=4 // common routines that can be inlined // // WARNING: all value -> register & pci routines have been coded // to standard AT&T direction, ie, out(value,reg) etc. // #define COMMON
#define PCI_DEVFN(slot,func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) #define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f) #define PCI_FUNC(devfn) ((devfn) & 0x07)
#define NORTH_BRIDGE_BUS 0 #define NORTH_BRIDGE_DEV 0 #define NORTH_BRIDGE_FUN 0 #define NORTH_BRIDGE_DEVFN PCI_DEVFN(NORTH_BRIDGE_DEV,NORTH_BRIDGE_FUN);
#define LPC_BRIDGE_BUS 0 #define LPC_BRIDGE_DEV 1 #define LPC_BRIDGE_FUN 0 #define LPC_BRIDGE_DEVFN PCI_DEVFN(LPC_BRIDGE_DEV,LPC_BRIDGE_FUN);
#define PCI_PCI_BRIDGE_BUS 0 #define PCI_PCI_BRIDGE_DEV 2 #define PCI_PCI_BRIDGE_FUN 0 #define PCI_PCI_BRIDGE_DEVFN PCI_DEVFN(PCI_PCI_BRIDGE_DEV,PCI_PCI_BRIDGE_FUN);
#define CONFIG_CMD(bus,devfn, where) (0x80000000 | (bus << 16) | (devfn << 8) | where )
extern inline void outb (unsigned char value, unsigned short port) { asm volatile ("outb %b0,%w1": :"a" (value), "Nd" (port)); }
extern inline void outl (unsigned int value, unsigned short port) { asm volatile ("outl %0,%w1": :"a" (value), "Nd" (port)); }
extern inline unsigned char inb (unsigned short port) { unsigned char _v asm ("al"); asm volatile ("inb %w1,%0":"=a" (_v):"Nd" (port)); return _v; }
static inline void pci_conf1_write_config_byte(unsigned char value, int addr) { outl(addr & ~3, 0xCF8); outb(value, 0xCFC + (addr & 3)); }
static inline unsigned char pci_conf1_read_config_byte(int addr) { outl(addr & ~3, 0xCF8); return inb(0xCFC + (addr & 3)); }
// // by. S. Gehlbach // console routines // edit with :set ts=4 // #define asm __asm__ #define volatile __volatile__ #define inline __inline__
/* Base Address */ #ifndef TTYS0_BASE #define TTYS0_BASE 0x3f8 #endif
#ifndef TTYS0_BAUD #define TTYS0_BAUD 115200 #endif
#if ((115200%TTYS0_BAUD) != 0) #error Bad ttys0 baud rate #endif
#define TTYS0_DIV (115200/TTYS0_BAUD)
/* Line Control Settings */ #ifndef TTYS0_LCS /* Set 8bit, 1 stop bit, no parity */ #define TTYS0_LCS 0x3 #endif
#define UART_LCS TTYS0_LCS
/* Data */ #define UART_RBR 0x00 #define UART_TBR 0x00
/* Control */ #define UART_IER 0x01 #define UART_IIR 0x02 #define UART_FCR 0x02 #define UART_LCR 0x03 #define UART_MCR 0x04 #define UART_DLL 0x00 #define UART_DLM 0x01
/* Status */ #define UART_LSR 0x05 #define UART_MSR 0x06 #define UART_SCR 0x07
#ifndef COMMON #include "common.h" #endif
extern inline int uart_can_tx_byte() { return inb(TTYS0_BASE + UART_LSR) & 0x20; }
extern inline void uart_wait_to_tx_byte() { while(!uart_can_tx_byte()) ; }
extern inline void uart_wait_until_sent() { while(!(inb(TTYS0_BASE + UART_LSR) & 0x40)) ; }
extern inline void uart_tx_byte(unsigned char data) { uart_wait_to_tx_byte(); outb(data, TTYS0_BASE + UART_TBR); /* Make certain the data clears the fifos */ uart_wait_until_sent(); }
extern inline int uart_have_rx_byte() { return inb(TTYS0_BASE + UART_LSR) & 0x1; }
extern inline void uart_enable_rx_byte() { /* say we are ready for a byte */ outb(0x02 | inb(TTYS0_BASE + UART_MCR), TTYS0_BASE + UART_MCR); }
extern inline void uart_disable_rx_byte() { /* say we aren't ready for another byte */ outb(~0x02 & inb(TTYS0_BASE + UART_MCR), TTYS0_BASE + UART_MCR); }
extern inline void uart_wait_for_rx_byte() { uart_enable_rx_byte(TTYS0_BASE); while(!uart_have_rx_byte(TTYS0_BASE)) ; uart_disable_rx_byte(); }
extern inline unsigned char uart_rx_byte() { static unsigned char data asm ("cl"); if (!uart_have_rx_byte(TTYS0_BASE)) { uart_wait_for_rx_byte(TTYS0_BASE); } data = inb(TTYS0_BASE + UART_RBR); return data; }
extern inline void uart_init() { /* disable interrupts */ outb(0x0, TTYS0_BASE + UART_IER); /* enable fifo's */ outb(0x01, TTYS0_BASE + UART_FCR); /* Set Baud Rate Divisor to 12 ==> 115200 Baud */ outb(0x80 | UART_LCS, TTYS0_BASE + UART_LCR); outb(TTYS0_DIV & 0xFF, TTYS0_BASE + UART_DLL); outb((TTYS0_DIV >> 8) & 0xFF, TTYS0_BASE + UART_DLM); outb(UART_LCS, TTYS0_BASE + UART_LCR); }
extern inline void uart_tx_bytes(char *data, unsigned len) { do { uart_wait_to_tx_byte(); outb(*data, TTYS0_BASE + UART_TBR); data++; len--; } while(len); uart_wait_until_sent(); }
extern inline unsigned long uart_rx_bytes(char * buffer, unsigned long size) { unsigned long bytes asm ("ecx"); bytes = 0; if (size == 0) { return 0; } if (!uart_have_rx_byte()) { uart_wait_for_rx_byte(); } do { buffer[bytes++] = inb(TTYS0_BASE + UART_RBR); } while((bytes < size) && uart_have_rx_byte()); return bytes; }
// // edit with tab stops = 4 (:set ts=4) // main setup // compile with: // gcc -O3 -S -fcall-used-esi -fcall-used-edi -fcall-used-ebx -fomit-fomit-frame-pointer -fverbose-asm -Winline // check for stack pushes with "grep esp setup.s" and "grep push setup.s". // Should only push/pop ebp which can be deleted. //
#include "console.h" #include "raminit.c" #include "chipinit.c" int main () { static char msg1[] = "Test output"; static char msg2[] = "Test output2"; static char msg3[] = "Test output3"; uart_init(); uart_tx_bytes(msg1,sizeof(msg1)); setup_chip(); uart_tx_bytes(msg2,sizeof(msg2)); setup_ram(); uart_tx_bytes(msg3,sizeof(msg3)); }
/* * 630_regs.h : Register Recommended Setting for SiS 630 [ABE][01] * * * Copyright 2000 Silicon Integrated Systems Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * * Reference: * 1. SiS 630 Specification * 2. SiS 630A1 Register Recommended Setting * Rev 0.97, Jan 7, 2000 * * $Id$ * * modd by S. Gehlbach <steve @ kesa . com> */
const unsigned char northbridge_init_table[] = { // Value -> Register /* generic PCI configuration space */ 0x07, 0x04, // Turn on Bus Master, 0x00, 0x05, // Memory and I/O Space 0x00, 0x06, // clear PCI status 0x00, 0x07, // 0x20, 0x0D, // Master Latency Timer = 32 PCI CLCK
/* SiS 630 specific registers. See SiS 630 Registers Recommended Setting */ /* Host Control Interface */ #ifdef ENABLE_SIS630_CPU_PIPELINE 0x9E, 0x50, // #else /* CPU Pipeline should be disable if on Coppermine CPU * or more than 1 Bus Master LAN */ 0x9C, 0x50, // #endif 0x00, 0x51, //
/* DRAM Control */ 0xC5, 0x52, // 0x00, 0x53, //
0x00, 0x54, // 0x00 -> 66/100 MHZ, 0x08 -> 133 MHZ 0x29, 0x55, // 0x29 -> 66/100 MHZ, 0x1D -> 133 MHZ 0x00, 0x56, // 0x00 -> 66 MHZ, 0x80 -> 100/133 MHZ 0x00, 0x57, // 0x00 -> 100 MHZ 0x01 -> 133 MHZ
/* Pre-driver Slew Rate/Current Driving Control */ 0x00, 0x58, // 0x35, 0x59, // 0x51, 0x5A, // 0x00, 0x5B, //
0x00, 0x65, // Use DIMM 0 for SMA
/* MISC Control */ 0xC0, 0x6A, // 0x01, 0x6B, // 0x00 -> 66/133 MHZ, 0x01 -> 100 MHZ 0x20, 0x6C, // 0x2E -> 66 MHZ, 0x20 -> 100 MHZ, 0x2C -> 133 MHZ
/* PCI Interface */ 0x21, 0x80, // 0xFF, 0x81, // 0x7F, 0x82, // 0x1E, 0x83, //
0x60, 0x84, // 0x00, 0x85, // 0x03, 0x86, // 0x40, 0x87, //
0x00, 0x88, // 0x08, 0x89, //
/* AGP GART Base Address */ 0x00, 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, 0x93,
/* Graphic Window Size */ 0x40, 0x94, // Graphic Window Size == 64MB 0x01, 0x97, // Page Table Cache Enable 0x00, 0x98, // 0x02, 0x9C, // Integrated VGA Enable
/* DRAM Priority Timer Control Register */ 0x00, 0xA0, 0x00, 0xA1, 0x03, 0xA2, 0x01, 0xA3,
/* AGP Command Register */ 0x04, 0xC8, // AGP 4X 0x00, 0xC9 // AGP Disabled };