⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 main.c

📁 linux驱动源码
💻 C
字号:
/********************************************************************
'	创建日期:	2006/04/18
'	文件名称: 	header.h
'	文件作者:	GENE.SHAO (E-Mail: sjiang1981@163.com)
'	
'	文件功能:	初始化内存控制器,I/O端口,将linux kernel从FLASH读到内存中,
				设置linux启动参数,然后跳转到内核的起始地址运行.
				
'   文件描述:	在使用该文件的时候请确保DOWNLOAD_ADDRESS,MEM_OFFSET,
				LINUX_PARAM_OFFSET和你的linux内核中设置的值一致.
				该程序在Samsung的s3c2410处理器上测试通过,你可以重新发
				布或修改该程序,如果对该程序有何疑问请通过Email联系作者。
*********************************************************************/

#include "header.h"

///linux参数结构, 是从linux内核中取出来的
/* This is the old deprecated way to pass parameters to the kernel */
struct param_struct {
    union {
	struct {
	    unsigned long page_size;		/*  0 */
	    unsigned long nr_pages;		/*  4 */
	    unsigned long ramdisk_size;		/*  8 */
	    unsigned long flags;		/* 12 */
#define FLAG_READONLY	1
#define FLAG_RDLOAD		4
#define FLAG_RDPROMPT	8
	    unsigned long rootdev;		/* 16 */
	    unsigned long video_num_cols;	/* 20 */
	    unsigned long video_num_rows;	/* 24 */
	    unsigned long video_x;		/* 28 */
	    unsigned long video_y;		/* 32 */
	    unsigned long memc_control_reg;	/* 36 */
	    unsigned char sounddefault;		/* 40 */
	    unsigned char adfsdrives;		/* 41 */
	    unsigned char bytes_per_char_h;	/* 42 */
	    unsigned char bytes_per_char_v;	/* 43 */
	    unsigned long pages_in_bank[4];	/* 44 */
	    unsigned long pages_in_vram;	/* 60 */
	    unsigned long initrd_start;		/* 64 */
	    unsigned long initrd_size;		/* 68 */
	    unsigned long rd_start;		/* 72 */
	    unsigned long system_rev;		/* 76 */
	    unsigned long system_serial_low;	/* 80 */
	    unsigned long system_serial_high;	/* 84 */
	    unsigned long mem_fclk_21285;       /* 88 */
	} s;
	char unused[256];
    } u1;
    union {
	char paths[8][128];
	struct {
	    unsigned long magic;
	    char n[1024 - sizeof(unsigned long)];
	} s;
    } u2;
    char commandline[1024];
};

//内存控制器初始值
static unsigned long memory_controller_inti_value[] =
{
	0x22111120,
	0x00000700,
	0x00000700,
	0x00000700,

	0x00000700,
	0x00000700,
	0x00000700,
	0x00018005,

	0x00018005,
	0x008E0459,
	0x000000B2,
	0x00000030,

	0x00000030,
};

void Delay(int time)
{
    int i;
    for(i=0;i<1000;i++);
}

//设置led灯, led1-led4, 0表示亮, 1表示灭
void set_led(int led1, int led2, int led3, int led4)
{
	(*(volatile char *) GPFDAT) = (led1 << 4) | (led2 << 5) | (led3 << 6) | (led4 << 7);
	return;
}

//等待NAND FLASH空闲
inline void wait_idle(void) 
{
    int i;
    while(!(rNFSTAT & BUSY))
      for(i=0; i<10; i++);
}

//初始化内存控制器
void init_memory_controller()
{
	int i;
	for (i=0; i<13; i++)
	{
		(*(volatile long *) (BWSCON + 4 * i)) = memory_controller_inti_value[i];
	}
	
}

//读取NAND FLASH, 将start_addr处开始的size个字节读到buf中.
int read_nand_flash(unsigned char *buf, unsigned long start_addr, int size)
{
    int i = 0, j;
	
	//要读取的FLASH首地址必须是按块对其的,且读取的大小为块的整倍数
    if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK))
	{
        return -1;	
    }
	
    //启用FLASH控制器
    rNFCONF &= ~0x800;    
	for(i=0; i<10; i++);

    for(i=start_addr; i < (start_addr + size);) 
	{
      //发送读命令
      rNFCMD = 0;

      //发送要读取的地址
      rNFADDR = i & 0xff;
      rNFADDR = (i >> 9) & 0xff;
      rNFADDR = (i >> 17) & 0xff;
      rNFADDR = (i >> 25) & 0xff;

      wait_idle();

	  //读取一个FLASH块
      for(j=0; j < NAND_SECTOR_SIZE; j++, i++) 
	  {
		  *buf = (rNFDATA & 0xff);
		  buf++;
      }
    }

    // 禁用FLASH控制器
    rNFCONF |= 0x800;
	
    return 0;
}

//初始化UART
void init_uart(int pclk,int baud)
{
    int i;
    if(pclk == 0)
	pclk    = PCLK;
    rUFCON0 = 0x0;  
    rUMCON0 = 0x0;  

    rULCON0 = 0x3;   
    rUCON0  = 0x245; 
    rUBRDIV0=( (int)(PCLK/16./115200) -1 );

    for(i=0;i<100;i++);
}
//发送一个字节
void uart_send_byte(int data)
{
        if(data=='\n')
        {
            while(!(rUTRSTAT0 & 0x2));
            Delay(10);                 //because the slow response of hyper_terminal 
             WrUTXH0('\r');
        }
        while(!(rUTRSTAT0 & 0x2));   //Wait until THR is empty.
        Delay(10);
        WrUTXH0(data);
} 
//一个字符串发送
void uart_send_string(char *pt)
{
    while(*pt)
        uart_send_byte(*pt++);
}

//重置NAND FLASH
void nand_flash_reset(void)
{
    int i;   
    
    NF_nFCE_L();
    NF_CMD(0xFF);

    for(i=0;i<10;i++); 

    NF_WAITRB();     
    NF_nFCE_H();
}
//初始化NAND FLASH
void nand_flash_init(void)
{
    rNFCONF = 0x0000F830;	   
    
    nand_flash_reset();
}

//初始化I/O端口和外部中断
void port_init(void)
{
    rGPACON = 0x7fffff; 
    rGPBUP  = 0x7ff; 

	rGPCCON = 0xaaaaaaaa;       
    rGPCUP  = 0xffff;  

    rGPDCON = 0xaaaaaaaa;       
    rGPDUP  = 0xffff;  

	rGPECON = 0xaaaaaaaa;       
    rGPEUP  = 0xffff;   

    rGPFCON = 0x55aa;
    rGPFUP  = 0xff;   

	rGPGCON = 0xff95ffba;
    rGPGUP  = 0xffff; 

	rGPHCON = 0x16faaa;
    rGPHUP  = 0x7ff;   
    
    rEXTINT0 = 0x22222222;   
    rEXTINT1 = 0x22222222;   
    rEXTINT2 = 0x22222222;   
}

//调用linux内核, a0: 0; a1: 处理器类型号; a2: 内核首地址
//关于协处理器15的设置,可参考<<ARM体系结构与编程>>第五章"ARM存储系统".
void  call_linux(long a0, long a1, long a2)
{	
__asm__(
	"mov	r0, %0\n"
	"mov	r1, %1\n"
	"mov	r2, %2\n"
	"mov	ip, #0\n"				
	"mcr	p15, 0, ip, c13, c0, 0\n"	//将PID置为0, 禁用快速上下文切换技术	
	"mcr	p15, 0, ip, c7, c7, 0\n"	//使I,D caches无效
	"mcr	p15, 0, ip, c7, c10, 4\n"	//清空写缓冲区
	"mcr	p15, 0, ip, c8, c7, 0\n"	//使I,D快表(TLB)无效
	"mrc	p15, 0, ip, c1, c0, 0\n"	//将寄存器c1中的内容读到ip(r12)中
	"bic	ip, ip, #0x0001\n"			//清除0位, 禁用MMU
	"mcr	p15, 0, ip, c1, c0, 0\n"	//将ip的内容写回c1
	"mov	pc, r2\n"					//跳转到内核的起始地址
	"nop\n"
	"nop\n"
	: 
	: "r" (a0), "r" (a1), "r" (a2)
	);
}

//自己定义的三个函数, 因为这里不能使用库函数.
//如果使用汇编语言写,可能效率会高一点. 但是这里只用来拷贝linux启动参数,足够用了.
int strlen(char * str)
{
	int i;
	for (i=0; i<1024; i++) {
		if (str[i] == 0) {
			return i;
		}
	}
	return 0;
}
void memcpy(char * dst, char * src, int len)
{
	int i;
	for (i=0; i<len; i++){
		*dst++ = *src++;
	}
}
void memset(char * dst, unsigned char data, int len)
{
	int i;
	for (i=0; i<len; i++) {
		*dst++ = data;
	}
}

//设置linux启动参数, 我的根文件系统为yaffs文件系统.
static void setup_linux_param(unsigned long param_base)
{
	struct param_struct *params = (struct param_struct *)param_base; 
	char linux_cmd[] = "noinitrd root=/dev/mtdblock/2 init=/linuxrc console=ttyS0";
		
	memset((char *)params, 0, sizeof(struct param_struct));

	params->u1.s.page_size = LINUX_PAGE_SIZE;
	params->u1.s.nr_pages = (DRAM_SIZE >> LINUX_PAGE_SHIFT);

	if (linux_cmd == NULL) {
	} else {
		memcpy(params->commandline, linux_cmd, strlen(linux_cmd) + 1);
	}
}

int main(int argc,char **argv)
{
	volatile unsigned char *kernel_start_addr;

	//打开第一个LED灯
	set_led(0, 1, 1, 1);	
	init_memory_controller();	
	port_init();
	
	//打开第二个LED灯
	set_led(1, 0, 1, 1);
	init_uart(PCLK, 115200);

	//打开第三个LED灯
	set_led(1, 1, 0, 1);
	kernel_start_addr = (unsigned char *)(DOWNLOAD_ADDRESS+MEM_OFFSET);	
	nand_flash_init();

	//打开第四个LED灯
	set_led(1, 1, 1, 0);
	//打印引导消息
	uart_send_string("This bootloader is wrote by ShaoJiang.\n");
	uart_send_string("If you have any question about the programme, please email to sjiang1981@163.com.\n");
	uart_send_string("Copy linux kernel image from 0x30000 to 0x30008000....\n");
	
	//拷贝linux内核
	read_nand_flash(kernel_start_addr, 0x30000, 1024 * 1024);
	uart_send_string("Begin to decompress linux kernel.\n");

	//设置linux启动参数
	setup_linux_param(DOWNLOAD_ADDRESS+LINUX_PARAM_OFFSET);

	//跳转到linux的起始地址
	call_linux(0, 193, DOWNLOAD_ADDRESS+MEM_OFFSET);
	return 0;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -