
/*
 *	FILE    	lnxstart.c
 *
 *	Version 	1.3
 *	Author  	Marc A. Viredaz
 *	        	Compaq Western Research Laboratory, Palo Alto, CA
 *	Date    	January 1999 (December 1998)
 *	System  	Itsy versions 1.0/1.1 and 1.5 (StrongARM SA-1100)
 *	Language	C
 *	Purpose 	This program updates the Linux parameter structure,
 *	        	according to the current run-time configuration, and
 *	        	start Linux.
 *
 *	Modified for booting Linux 2.4 by Charles B. Morrey III
 *                                        Michael Neufeld
 *					  
 *
 * Copyright (c) Compaq Computer Corporation, 1998, 1999
 */

/* avoid sucking in linux mmap defs */
#define	VirtAdd(x)	(x)
#define UART_SERIAL3_BASE_V     0xe0050000
#define SERIAL_ECHO_DIVISOR	0x17	/* 9600 baud */
#define SERIAL_ECHO_DIVISOR_38400	0x5	/* 38400 baud */
#define SERIAL_ECHO_DIVISOR_115200	0x1	/* 115200 baud */

#define UART_SERIAL3_BASE_P     0x80050000
#define UTCR0               0x00
#define UTCR1               0x04
#define UTCR2               0x08
#define UTCR3               0x0c
#define UTDR                0x14
#define UTSR0               0x1C   
#define UTSR1               0x20

#define UART_RX             UTDR
#define UART_TX             UTDR

#define UTCR3_RXE            1     /* Receiver enable */
#define UTCR3_TXE            2     /* Transmit enable */
#define UTSR1_TNF            4     /* transmit fifo non full */

#define GPSR_P		0x90040008    /* GPSR pin set register */

#define SA1100_UART3_UTSR1      (UART_SERIAL3_BASE_P + UTSR1)
#define SA1100_UART3_UTDR       (UART_SERIAL3_BASE_P + UTDR)
#define SA1100_UTSR1_TNF	(1 << 2)

#define SZ_1M                           0x00100000


/* XXX #undef __linux__ */
#include "../../../../include/asm-arm/arch-sa1100/memory.h"
#include "./ARMv4.h"
#include "./SA-1100.h"
#include "./Itsy.h"
#include "./monpar.h"
#include "./setup.h"
#include "./io.h"
#define serial_echo_outl(v,a) outl((v),(a)+UART_SERIAL3_BASE_P)

extern
struct param_struct	brutus_params ;

static const char COMMANDLINE[]="initrd ramdisk_size=16384";

void putc(char c);
void putstr(const char *str);
void printptr(unsigned long);
void tochar(unsigned char);

/*
 * PUBLIC PROCEDURE: Start
 *
 * Purpose
 *    The procedure "Start" is the main routine of the program "lnxstart".
 *
 * Input
 *    LnxStartAdd	Linux start address.
 *    RAMParamPtr	Pointer in RAM where to store the updated Linux
 *              	parameter structure.
 *    Arch      	Linux architecture code.
 *
 * Output       	-
 */

void Start (
   void         	(*LnxStartAdd) (struct param_struct *, Word, Word),
   struct param_struct	*RAMParamPtr,
   Word         	Arch)
{
   unsigned int 	Size, BnkNb, TotPage, NbWord ;
   Word         	*WordPtr1, *WordPtr2, FstWord ;


   outl(0x01800000, GPSR_P);
   serial_echo_outl(0x08, UTCR0);
   serial_echo_outl(SERIAL_ECHO_DIVISOR_115200 >> 8, UTCR1);
   serial_echo_outl(SERIAL_ECHO_DIVISOR_115200 & 0xff, UTCR2);
   serial_echo_outl(0, UTCR3);
   serial_echo_outl(UTCR3_TXE, UTCR3);
   serial_echo_outl(0xff, UTSR0);


   /* This should copy the kernel into DRAM. */
   WordPtr1 = (Word *) LnxStartAdd;
   WordPtr2 = (Word *) (RAMParamPtr);
   WordPtr2 += 8192;

   putstr("the start address of linux in DRAM is:\n");
   printptr((unsigned long)WordPtr2);
   printptr((unsigned long)RAMParamPtr);
   for (Size = 0 ; Size < SZ_1M ; Size += sizeof (Word)) {
     if (*(WordPtr1) == 0x57617364) {
       putstr("found our string at:");
       printptr((unsigned long)WordPtr1);
       printptr((unsigned long)WordPtr2);
     }
     *(WordPtr2++) = *(WordPtr1++) ;
   }
       
   WordPtr1 = (Word *) LnxStartAdd;
   WordPtr2 = (Word *) (RAMParamPtr);
   WordPtr2 += 8192;
   if (*WordPtr1 == *WordPtr2) {
     putstr("first words match\n");
   } else {
     putstr("first words don't match\n");
   }
   
   WordPtr1 = (Word *) &brutus_params ;
   RAMParamPtr = (struct param_struct*)(((char*)RAMParamPtr)+ 0x100);
   WordPtr2 = (Word *) RAMParamPtr ;
   putstr("Start address for params is:");
   printptr((unsigned long)WordPtr2);
   printptr((unsigned long)(&brutus_params));
   for (Size = 0 ; Size < sizeof (struct param_struct) ; Size += sizeof (Word)) {
     *(WordPtr2) = *(WordPtr1);
     WordPtr2++;
     WordPtr1++;
   }
   /*
   RAMParamPtr->u1.s.itsy_version = ItsyPar.ItsyVersion ;
   RAMParamPtr->u1.s.mon_version = ItsyPar.MonVersion ;
   for (BnkNb = 0 ; BnkNb < 4 ; BnkNb++)
   {
     RAMParamPtr->u1.s.class_id [BnkNb] = (ItsyPar.StMemPar [BnkNb]).Ident ;
     RAMParamPtr->u1.s.staticmem_info_pointers [BnkNb] =
        (param_u32) (ItsyPar.StMemPar [BnkNb]).CtrlTbl ;
   }
   */
   TotPage = 0 ;
   for (BnkNb = 0 ; BnkNb < 4 ; BnkNb++)
      if (MDCNFG & MDCNFG_DE (BnkNb))
      {
         FstWord = *(WordPtr1 = (Word *) DRAMBnk [BnkNb]) ;
         for (NbWord = 1 ; NbWord < DRAMBnkSp/sizeof (Word) ; NbWord *= 2)
            if (WordPtr1 [NbWord] == FstWord)
            {
               *WordPtr1 = ~FstWord ;
               if (WordPtr1 [NbWord] == ~FstWord)
               {
                  *WordPtr1 = FstWord ;
                  break ;
               }
               *WordPtr1 = FstWord ;
            }
         RAMParamPtr->u1.s.pages_in_bank [BnkNb] =
            (unsigned long) ((NbWord*sizeof (Word)) >> FSize (Add_SmPgIdx)) ;
         TotPage += (NbWord*sizeof (Word)) >> FSize (Add_SmPgIdx) ;
      }
      else
         RAMParamPtr->u1.s.pages_in_bank [BnkNb] = 0 ;
   RAMParamPtr->u1.s.nr_pages = TotPage ;
   /*
   RAMParamPtr->u1.s.dram_info_pointer = (param_u32) ItsyPar.DRAMCtrlTbl ;
   */

   printptr(RAMParamPtr->u1.s.pages_in_bank[0]);
   putstr(" in bank 0.\n");

   printptr(RAMParamPtr->u1.s.pages_in_bank[1]);
   putstr(" in bank 1.\n");

   printptr(RAMParamPtr->u1.s.pages_in_bank[2]);
   putstr(" in bank 2.\n");

   printptr(RAMParamPtr->u1.s.pages_in_bank[3]);
   putstr(" in bank 3.\n");

   printptr(RAMParamPtr->u1.s.nr_pages);
   putstr(" total pages.\n");
   
	
   putstr("commandline const is:\n");
   putstr(COMMANDLINE);
   putstr("\n");
   
   WordPtr2 = (Word*) RAMParamPtr->commandline;
   WordPtr1 = (Word*) COMMANDLINE;
   for (Size =0; Size < sizeof(COMMANDLINE); Size += sizeof(Word)) {
     *(WordPtr2++) = *(WordPtr1++) ;
   }

   putstr("commandline in paramstruct is:\n");
   putstr(RAMParamPtr->commandline);
   putstr("\n");
   putstr("pointer of paramstruct->commandline is\n");
   printptr((unsigned long)(RAMParamPtr->commandline));
   putstr("printing of hex address 0xC0000600 is \n");
   putstr((char*)0xC0000600);

   putstr("\n");

   WordPtr1 = (Word*) RAMParamPtr->u1.s.initrd_size;
   putstr("size to copy is:\n");
   printptr((unsigned long)WordPtr1);
   
   WordPtr1 = (Word*) RAMParamPtr->u1.s.page_size;
   putstr("page_size is:\n");
   printptr((unsigned long)WordPtr1);

   WordPtr1 = (Word*) RAMParamPtr->u1.s.nr_pages;
   putstr("nr_pages in params struct is:\n");
   printptr((unsigned long)WordPtr1);

   WordPtr2 = (Word*) 0xC8000000;
   WordPtr1 = (Word*) RAMParamPtr->u1.s.initrd_start;
   putstr("start address to copy from is:\n");
   printptr((unsigned long)WordPtr1);

   for (Size=0;Size< RAMParamPtr->u1.s.initrd_size; Size += sizeof(Word)) {
     *(WordPtr2++) = *(WordPtr1++) ;
   }

   /* This is the virtual address of 0xC8000000 in Linux 2.4 */
   RAMParamPtr->u1.s.initrd_start = (unsigned long)__phys_to_virt(0xC8000000);
   /* This is used as a block offset into the initrd */
   RAMParamPtr->u1.s.rd_start = (unsigned long)0x0;
   
   putstr("kernel start address and RAM start address.");
   printptr((unsigned long)LnxStartAdd);
   printptr((unsigned long)RAMParamPtr);
   putstr("initrd start in this version of linux:");
   printptr((unsigned long)RAMParamPtr->u1.s.initrd_start);
   printptr(Arch);
   putstr(": is architecture number\n");

   WordPtr2 = (Word *) (RAMParamPtr);
   WordPtr2 -= 64;
   WordPtr2 += 8192;
   LnxStartAdd = (void*)WordPtr2;
   putstr("about to jump to linux\n");
   (*LnxStartAdd) (0, Arch, WordPtr2);
}


void putc(char c) 
{
/* wait for space in the TX FIFO */
     while (!((*(volatile long *)SA1100_UART3_UTSR1) & SA1100_UTSR1_TNF)); /* wait until TX FIFO not full */
  *(char *)SA1100_UART3_UTDR = c;
}

void putstr(const char *str)
{
  if (str == NULL)
    return;
  while (*str != '\0') {
    putc(*str) ;
    str++ ;
  }
}


void tochar(unsigned char c) {
  unsigned char n0, n1;

  n0 = c & 0x0F;
  n1 = (c >> 4) & 0x0F;

  n0 = ((n0 <= 9) ? (n0 + '0') : (n0 + 'A' - 10));
  n1 = ((n1 <= 9) ? (n1 + '0') : (n1 + 'A' - 10));

  putc(n1);
  putc(n0);
}


void printptr(unsigned long thepointer) {
  unsigned char byte0,byte1,byte2,byte3;

  byte0 = thepointer & 0xFF;
  byte1 = (thepointer >> 8) & 0xFF;
  byte2 = (thepointer >> 16) & 0xFF;
  byte3 = (thepointer >> 24) & 0xFF;

  putc('0');
  putc('x');
  tochar(byte3);
  tochar(byte2);
  tochar(byte1);
  tochar(byte0);
  putc('\n');
}
