顯示具有 嵌入式系統初學筆記 標籤的文章。 顯示所有文章
顯示具有 嵌入式系統初學筆記 標籤的文章。 顯示所有文章

2011年6月22日 星期三

mini2440-GPIO驅動相關筆記

GPIO驅動相關筆記

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/llxmedici/archive/2011/03/27/6282372.aspx

打算跟著友善之臂的《mini2440 linux移植開發指南》來做個LED驅動,雖然LED的原理簡單得不能再簡單了,但是要把kernel中針對於s3c24**的GPIO的一些資料結構,還有函數搞清楚也不是那麼輕鬆的事,所以本文主要簡單地說明下LED驅動中的相關資料結構以及函數/巨集的定義,並對驅動加以驗證

***************************************************************************

注意:在/arch/arm/mach-s3c2410/include/mach/gpio-fns.h源代碼中有如下說明:
16/* These functions are in the to-be-removed category and it is strongly
17 * encouraged not to use these in new code. They will be marked deprecated
18 * very soon.
19 *
20 * Most of the functionality can be either replaced by the gpiocfg calls
21 * for the s3c platform or by the generic GPIOlib API.
22 *
23 * As of 2.6.35-rc, these will be removed, with the few drivers using them
24 * either replaced or given a wrapper until the calls can be removed.
25*/

該頭檔包括:
static inline void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int cfg)

該函數直接使用
linux/arch/arm/plat-s3c/gpio-config.c中的
int s3c_gpio_cfgpin(unsigned int pin, unsigned int config)
即可

***************************************************************************
首先看一下設備初始化程式:
85 /*
86 * 設備初始化
87 */
88 static int __init dev_init(void)
89 {
90 int ret;
91 int i;
92 for (i = 0; i < 4; i++) {
93 //設置 LED 對應的埠寄存器為輸出(OUTPUT)
94 if (s3c_gpio_cfgpin(led_table[i], led_cfg_table[i])<0)
95 printk(KERN_INFO "config pin %d failed", i);
95 //設置 LED 對應的埠寄存器為低電平輸出,在模組載入> 結束後,四個 LED 應該是全部都是發光
96 狀態
97 s3c2410_gpio_setpin(led_table[i], 0);
98 }
99 ret = misc_register(&misc); //註冊設備
100 printk (DEVICE_NAME"\tinitialized\n"); //列印初始化資訊
101 return ret;
102 }
可以看到,這裏涉及到兩個函數,分別是s3c2410_gpio_cfgpin , s3c2410_gpio_setpin,這兩個函數分別對四個LED進行配置,從函數名來看,cfgpin對引腳寄存器狀態進行配置,而setpin應該是對寄存器資料值進行配置,我們在分析函數之前先弄清楚傳入的參數到底是什麼。

led_table[i]

28 //LED 對應的 GPIO 埠列表

29 static unsigned long led_table [] = {

30 S3C2410_GPB(5),

31 S3C2410_GPB(6),

32 S3C2410_GPB(7),

33 S3C2410_GPB(8),

34 };



這裏S3C2410_GPB巨集定義在mach/gpio-nrs.h中

/* GPIO bank sizes */

#define S3C2410_GPIO_A_NR (32)

#define S3C2410_GPIO_B_NR (32)

#define S3C2410_GPIO_C_NR (32)

#define S3C2410_GPIO_D_NR (32)

#define S3C2410_GPIO_E_NR (32)

#define S3C2410_GPIO_F_NR (32)

#define S3C2410_GPIO_G_NR (32)

#define S3C2410_GPIO_H_NR (32)

#define S3C2410_GPIO_J_NR (32) /* technically 16. */

#define S3C2410_GPIO_K_NR (32) /* technically 16. */

#define S3C2410_GPIO_L_NR (32) /* technically 15. */

#define S3C2410_GPIO_M_NR (32) /* technically 2. */



#if CONFIG_S3C_GPIO_SPACE != 0

#error CONFIG_S3C_GPIO_SPACE cannot be zero at the moment

#endif



#define S3C2410_GPIO_NEXT(__gpio) \

((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 0)

//這裏的CONFIG_S3C_GPIO_SPAC是內核配置選項,在.config中可以找到,我的配置為:

CONFIG_S3C_GPIO_SPACE = 0



enum s3c_gpio_number {

S3C2410_GPIO_A_START = 0,

S3C2410_GPIO_B_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_A),

S3C2410_GPIO_C_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_B),

S3C2410_GPIO_D_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_C),

S3C2410_GPIO_E_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_D),

S3C2410_GPIO_F_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_E),

S3C2410_GPIO_G_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_F),

S3C2410_GPIO_H_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_G),

S3C2410_GPIO_J_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_H),

S3C2410_GPIO_K_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_J),

S3C2410_GPIO_L_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_K),

S3C2410_GPIO_M_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_L),

};



#define S3C2410_GPB(_nr) (S3C2410_GPIO_B_START + (_nr))

因此,以S3C2410_GPB(5)為例,其宏展開為:

S3C2410_GPIO_NEXT(S3C2410_GPIO_A) +5 =>

(S3C2410_GPIO_A_START + S3C2410_GPIO_A_NR + CONFIG_S3C_GPIO_SPACE + 0) + 5 =>

很顯然, S3C2410_GPB(5)就是從GPA的首地址+GPA個數+GPB的offset就是當前GPB的IO偏移量,即

0+32+5=37, 同理

S3C2410_GPB(0) 相當於 32

30 S3C2410_GPB(5) 相當於 37

31 S3C2410_GPB(6) 相當於 38

32 S3C2410_GPB(7) 相當於 39

33 S3C2410_GPB(8) 相當於 40

***************************************************************************
led_cfg_table[i]
36 //LED 對應埠將要輸出的狀態列表
37 static unsigned int led_cfg_table [] = {
38 S3C2410_GPIO_OUTPUT,
39 S3C2410_GPIO_OUTPUT,
40 S3C2410_GPIO_OUTPUT,
41 S3C2410_GPIO_OUTPUT,
42 };
S3C2410_GPIO_OUTPUT定義在mach/regs-gpio.h
#define S3C2410_GPIO_LEAVE (0xFFFFFFFF) // 最後兩位是設置,11表示RESERVE
#define S3C2410_GPIO_INPUT (0xFFFFFFF0) /* not available on A */ // 最後兩位是設置,00表示INPUT
#define S3C2410_GPIO_OUTPUT (0xFFFFFFF1) // 最後兩位是設置,01表示OUTPUT
#define S3C2410_GPIO_IRQ (0xFFFFFFF2) /* not available for all */
#define S3C2410_GPIO_SFN2 (0xFFFFFFF2) /* bank A => addr/cs/nand */
#define S3C2410_GPIO_SFN3 (0xFFFFFFF3) /* not available on A */

***************************************************************************
根據前面的分析,s3c2410傳入了當前GPIO的偏移位址,以及OUTPUT狀態
現在我們深入前面的兩個函數:
定義在linux/arch/arm/plat-s3c/gpio-config.c
int s3c_gpio_cfgpin(unsigned int pin, unsigned int config)
{
struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin); //得到對應GPIO結構體首指標,裏面包含了該GPIO的各種參數
unsigned long flags;
int offset;
int ret;

if (!chip) return -EINVAL; // 沒找到的話,返回invalid
offset = pin - chip->chip.base; // 否則offset等於該GPIO引腳相對於GPX(0)的偏移量,每個偏移1
s3c_gpio_lock(chip, flags); // 自旋鎖鎖住該GPIO,通過chip指標指向lock,看下面的define和圖
ret = s3c_gpio_do_setcfg(chip, offset, config); //設置該GPIO狀態寄存器的數值為config
s3c_gpio_unlock(chip, flags); // 解鎖

// 自旋鎖操作
/* locking wrappers to deal with multiple access to the same gpio bank */
//#define s3c_gpio_lock(_oc, _fl) spin_lock_irqsave(&(_oc)->lock, _fl)
//#define s3c_gpio_unlock(_oc, _fl) spin_unlock_irqrestore(&(_oc)->lock, _fl)
//s3c_gpio_do_setcfg操作
static inline int s3c_gpio_do_setcfg(struct s3c_gpio_chip *chip,
unsigned int off, unsigned int config)
{
return (chip->config->set_config)(chip, off, config);
}
//這裏的set_config是一個函數指標,由後面的分析知道,如果針對GPA,該函數指標指向s3c_gpio_setcfg_s3c24xx_a , 如果針對GPX應該是指向s3c_gpio_setcfg_s3c24xx——但發現,如果是其他GPX,根本沒有定義set_config!!! (這個問題已經解決,見後文s3c24xx_gpiolib_init函數,事實上,其餘的config的確指向s3c_gpio_do_setcfg函數)

struct s3c_gpio_cfg s3c24xx_gpiocfg_default = {
.set_config = s3c_gpio_setcfg_s3c24xx,
.get_config = s3c_gpio_getcfg_s3c24xx,
};

int s3c_gpio_setcfg_s3c24xx_a(struct s3c_gpio_chip *chip, unsigned int off, unsigned int cfg)
{
void __iomem *reg = chip->base; // GPXCON的物理基底位址
unsigned int shift = off; // 每個GPA對應一位
u32 con;
if (s3c_gpio_is_cfg_special(cfg)) { //OUTPUT狀態是否為(0xfffffffX),是,返回1
cfg &= 0xf; // cfg = 0xX
/* Map output to 0, and SFN2 to 1 */ 本實驗不會運行到這
cfg -= 1;
if (cfg > 1) return -EINVAL;
cfg <<= shift;
}
con = __raw_readl(reg); // 先讀出該GPXCON的值,32位
con &= ~(0x1 << shift); //
con //
__raw_writel(con, reg); // 將新值寫入GPXCON

PS:
#define __raw_writeb(v,a) (__chk_io_ptr(a), *(volatile unsigned char __force *)(a) = (v))
#define __raw_writew(v,a) (__chk_io_ptr(a), *(volatile unsigned short __force *)(a) = (v))
#define __raw_writel(v,a) (__chk_io_ptr(a), *(volatile unsigned int __force *)(a) = (v))
#define __raw_readb(a) (__chk_io_ptr(a), *(volatile unsigned char __force *)(a))
#define __raw_readw(a) (__chk_io_ptr(a), *(volatile unsigned short __force *)(a))
#define __raw_readl(a) (__chk_io_ptr(a), *(volatile unsigned int __force *)(a))
return 0;
}

如果針對GPX情況
int s3c_gpio_setcfg_s3c24xx(struct s3c_gpio_chip *chip,
unsigned int off, unsigned int cfg)
{
void __iomem *reg = chip->base;
unsigned int shift = off * 2; // 每個GPX對應2位
u32 con;
if (s3c_gpio_is_cfg_special(cfg)) {
cfg &= 0xf;
if (cfg > 3)return -EINVAL;
cfg <<= shift; // 將cfg的0,1兩位左移offset
}
con = __raw_readl(reg); // 讀對應的GPXCON值
con &= ~(0x3 << shift); // 將GPXCON(pin)的兩bits請0
con = cfg; // 設置config值

__raw_writel(con, reg); // 寫入新的GPXCON

return 0;
}

return ret;
} // end s3c_gpio_cfgpin
這裏涉及到了一個重要的資料結構,s3c_gpio_chip,此資料結構比較複雜,我貼出這個資料結構的結構圖一:

這個重要的資料結構中可以記錄每個GPIO所需要的所有資料,後面會遇到的s3c24xx_gpios[]結構體就是該結構體的集合,描述了晶片中所有的GPIO埠,之後我們需要時時回頭看看這個結構。

我們先來看s3c_gpiolib_getchip ,它實現了返回對應pin值的GPIO結構體首指標的功能

#include

static inline struct s3c_gpio_chip *s3c_gpiolib_getchip(unsigned int pin)

{

struct s3c_gpio_chip *chip;



if (pin > S3C_GPIO_END) //如果超過GPJ(32)就return NULL

return NULL;

chip = &s3c24xx_gpios[pin/32]; //根據偏移,計算出對應pin的GPIO結構體指標

return ((pin - chip->chip.base) < chip->chip.ngpio) ? chip : NULL;// 這裏驗證,如果pin偏移超過了GPIO的個數,說明出錯了,否則就返回該GPIO的結構體指標

}

回想以下之前s3c2410_gpio_cfgpin中,我們傳入的參數是led_table[i]和 led_cfg_table[i],

/* GPIO sizes for various SoCs:

*

* 2442

* 2410 2412 2440 2443 2416

* ---- ---- ---- ---- ----

* A 23 22 25 16 25

* B 11 11 11 11 9

* C 16 15 16 16 16

* D 16 16 16 16 16

* E 16 16 16 16 16

* F 8   8   8   8   8

* G16 16 16 16 8

* H 11 11 9 15 15

* J -- -- 13 16 --

* K -- -- -- -- 16

* L -- -- -- 15 7

* M -- -- -- 2 2

*/

struct s3c_gpio_chip s3c24xx_gpios[] = {

[0] = {

.base = S3C2410_GPACON, // datasheet上地址為0x56000000

//#define S3C2410_GPACON S3C2410_GPIOREG(0x00)

#define S3C2410_GPIOREG(x) ((x) + S3C24XX_VA_GPIO)

#define S3C24XX_VA_GPIO ((S3C24XX_PA_GPIO - S3C24XX_PA_UART) + S3C24XX_VA_UART)

S3C24XX_PA_GPIO相當於(0x15600000)

S3C24XX_PA_UART相當於(0x15000000)

#define S3C_VA_UART S3C_ADDR(0x01000000) /* UART */

#define S3C_ADDR_BASE 0xF6000000



#ifndef __ASSEMBLY__

#define S3C_ADDR(x) ((void __iomem __force *)S3C_ADDR_BASE + (x))

#else

#define S3C_ADDR(x) (S3C_ADDR_BASE + (x))

#endif

0x15600000-15000000+F7000000 這裏的S3C2410_GPACON應該怎麼算?

.pm = __gpio_pm(&s3c_gpio_pm_1bit),

.config = &s3c24xx_gpiocfg_banka, // 設置GPIO的函數指標

static struct s3c_gpio_cfg s3c24xx_gpiocfg_banka = {

.set_config = s3c_gpio_setcfg_s3c24xx_a,

.get_config = s3c_gpio_getcfg_s3c24xx_a,

};

.chip = {

.base = S3C2410_GPA(0), //基底位址,也是偏移量

.owner = THIS_MODULE,

.label = "GPIOA",

.ngpio = 24,

.direction_input = s3c24xx_gpiolib_banka_input,

.direction_output = s3c24xx_gpiolib_banka_output,

},

},

[1] = {

.base = S3C2410_GPBCON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.chip = {

.base = S3C2410_GPB(0),

.owner = THIS_MODULE,

.label = "GPIOB",

.ngpio = 16,

},

},

[2] = {

.base = S3C2410_GPCCON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.chip = {

.base = S3C2410_GPC(0),

.owner = THIS_MODULE,

.label = "GPIOC",

.ngpio = 16,

},

},

[3] = {

.base = S3C2410_GPDCON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.chip = {

.base = S3C2410_GPD(0),

.owner = THIS_MODULE,

.label = "GPIOD",

.ngpio = 16,

},

},

[4] = {

.base = S3C2410_GPECON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.chip = {

.base = S3C2410_GPE(0),

.label = "GPIOE",

.owner = THIS_MODULE,

.ngpio = 16,

},

},

[5] = {

.base = S3C2410_GPFCON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.chip = {

.base = S3C2410_GPF(0),

.owner = THIS_MODULE,

.label = "GPIOF",

.ngpio = 8,

.to_irq = s3c24xx_gpiolib_bankf_toirq,

},

},

[6] = {

.base = S3C2410_GPGCON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.irq_base = IRQ_EINT8,

.chip = {

.base = S3C2410_GPG(0),

.owner = THIS_MODULE,

.label = "GPIOG",

.ngpio = 16,

.to_irq = samsung_gpiolib_to_irq,

},

}, {

.base = S3C2410_GPHCON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.chip = {

.base = S3C2410_GPH(0),

.owner = THIS_MODULE,

.label = "GPIOH",

.ngpio = 11,

},

},

/* GPIOS for the S3C2443 and later devices. */2440用不到

{

.base = S3C2440_GPJCON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.chip = {

.base = S3C2410_GPJ(0),

.owner = THIS_MODULE,

.label = "GPIOJ",

.ngpio = 16,

},

}, {

.base = S3C2443_GPKCON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.chip = {

.base = S3C2410_GPK(0),

.owner = THIS_MODULE,

.label = "GPIOK",

.ngpio = 16,

},

}, {

.base = S3C2443_GPLCON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.chip = {

.base = S3C2410_GPL(0),

.owner = THIS_MODULE,

.label = "GPIOL",

.ngpio = 15,

},

}, {

.base = S3C2443_GPMCON,

.pm = __gpio_pm(&s3c_gpio_pm_2bit),

.chip = {

.base = S3C2410_GPM(0),

.owner = THIS_MODULE,

.label = "GPIOM",

.ngpio = 2,

},

},

};

***************************************************************************
下面分析第二個函數,先看一下相關結構體

圖二:gpio_desc和gpio_chip結構圖

void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)

{

/* do this via gpiolib until all users removed */



gpio_request(pin, "temporary");

gpio_set_value(pin, to);

gpio_free(pin);

}

又出現了三個函數,我們一一說明:

1169/* These "optional" allocation calls help prevent drivers from stomping

1170 * on each other, and help provide better diagnostics in debugfs.

1171 * They're called even less than the "set direction" calls.

1172 */

PS:static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];

其中ARCH_NR_GPIOS在arch/arm/mach-s3c2410/include/mach/gpio.h中定義
#define ARCH_NR_GPIOS (32 * 9 + CONFIG_S3C24XX_GPIO_EXTRA)
因此,每個引腳都分配了一個gpio_desc資料結構
1173int gpio_request(unsigned gpio, const char *label) // 這個函數還不是很明白
1174{
1175 struct gpio_desc *desc;
1176 struct gpio_chip *chip;
1177 int status = -EINVAL;
1178 unsigned long flags;
1179
1180 spin_lock_irqsave(&gpio_lock, flags); // gpio_lock是自旋鎖,上鎖,保存FLAG在flags變數中
1181
1182 if (!gpio_is_valid(gpio)) // 不符合要求,跳轉到done
1183 goto done;
1184 desc = &gpio_desc[gpio]; // desc = &gpio_desc[pin]
1185 chip = desc->chip;
1186 if (chip == NULL) // gpio_desc.chip指向NULL,跳轉到done
1187 goto done;
1188
1189 if (!try_module_get(chip->owner)) // 該函數用於增加模組使用計數;若返回為0,表示調用失敗,希望使用的模組沒有被載入或正在被卸載中
1190 goto done;
1191
1192 /* NOTE: gpio_request() can be called in early boot,
1193 * before IRQs are enabled, for non-sleeping (SOC) GPIOs.
1194 */
1195
1196 if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) { // 原子操作,將flags的第FLAG_REQUESTED位置1,並返回其原值
1197 desc_set_label(desc, label ? : "?"); // 如果原來的值是0, 執行desc_set_label, 對desc->chip.label賦值,如果label有定義,直接用定義,比如上面的“temporary”,否則用“?”
static inline void desc_set_label(struct gpio_desc *d, const char *label)
{
#ifdef CONFIG_DEBUG_FS
d->label = label; // 為什麼不是d->chip.label = label; ?
#endif
}

1198 status = 0;
1199 } else { // 如果flags的第FLAG_REQUESTED位原來的值是1
1200 status = -EBUSY;
1201 module_put(chip->owner); // 該函數用於減少模組使用計數
1202 goto done;
1203 }
1204
1205 if (chip->request) { // chip->request在linux初始化時是沒有指向的,可以見後面s3c_gpiolib_add
1206 /* chip->request may sleep */
1207 spin_unlock_irqrestore(&gpio_lock, flags); // 如果chip->request不為0, 解鎖,因為後面調用的chip->request有可能睡眠
1208 status = chip->request(chip, gpio - chip->base);
1209 spin_lock_irqsave(&gpio_lock, flags); // 執行完後,繼續上鎖
1210
1211 if (status < 0) { // status返回負數,說明出錯
1212 desc_set_label(desc, NULL);
1213 module_put(chip->owner);
1214 clear_bit(FLAG_REQUESTED, &desc->flags);
1215 }
1216 }
1217
1218done:
1219 if (status)
1220 pr_debug("gpio_request: gpio-%d (%s) status %d\n", gpio, label ? : "?", status);
1221 // 如果狀態不為0, 列印gpio-pin"****"的狀態
1222 spin_unlock_irqrestore(&gpio_lock, flags); // 解鎖
1223 return status; // 返回狀態
1224}

***************************************************************************
下面先分析gpio_free函數
void gpio_free(unsigned gpio) // 待分析
{

unsigned long flags;
struct gpio_desc *desc;
struct gpio_chip *chip;
might_sleep();

if (!gpio_is_valid(gpio)) {
WARN_ON(extra_checks);
return;
}

gpio_unexport(gpio);
spin_lock_irqsave(&gpio_lock, flags);
desc = &gpio_desc[gpio];
chip = desc->chip;
if (chip && test_bit(FLAG_REQUESTED, &desc->flags)) {
if (chip->free) {
spin_unlock_irqrestore(&gpio_lock, flags);
might_sleep_if(chip->can_sleep);
chip->free(chip, gpio - chip->base);
spin_lock_irqsave(&gpio_lock, flags);
}

desc_set_label(desc, NULL);
module_put(desc->chip->owner);
clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
clear_bit(FLAG_REQUESTED, &desc->flags);
} else
WARN_ON(extra_checks);

spin_unlock_irqrestore(&gpio_lock, flags);
}

EXPORT_SYMBOL_GPL(gpio_free);

***************************************************************************
arch/arm/mach-s3c2410/include/mach/gpio.h
#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value)
{
struct gpio_chip *chip;
chip = gpio_to_chip(gpio); // 返回對應於pin的gpio_desc[pin].chip指針
WARN_ON(chip->can_sleep);
chip->set(chip, gpio - chip->base, value); // 這裏調用的是s3c_gpiolib_set函數!!!

}

/* caller holds gpio_lock *OR* gpio is marked as requested */
static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
{
return gpio_desc[gpio].chip;
}

看到這裏,一直有個問題讓我百思不得其解,這裏的chip按理說應該是s3c_gpio_chip中的chip成員,但是之前都沒有代碼交代他們是如何聯繫到一起的,s3c_gpio_chip與gpio_desc結構體如何聯繫在一起,也沒有函數交代,並且這裏的chip->set函數指標也沒有實現的代碼,但是,經實驗確認沒有問題後,我開始查找plat-s3c24xx/gpiolib.c中的函數希望能有些線索,果然,找到了這麼一個函數:

static __init int s3c24xx_gpiolib_init(void)
{
struct s3c_gpio_chip *chip = s3c24xx_gpios;
int gpn;
for (gpn = 0; gpn < ARRAY_SIZE(s3c24xx_gpios); gpn++, chip++) {
if (!chip->config)
chip->config = &s3c24xx_gpiocfg_default; // 原來chip->config默認函數也是在這裏!!!

s3c_gpiolib_add(chip); // 之前的疑惑都在這裏實現!!!
}
return 0;
}

core_initcall(s3c24xx_gpiolib_init);
但是,這個s3c24xx_gpiolib_init函數又是在什麼時候執行的呢?可以看到,在該函數的下面,有一句:core_initcall(s3c24xx_gpiolib_init); 查閱相關資料發現, 在linux初始化的過程中,內核採用了一種initcall的機制,它利用gcc的擴展功能以及ld的連接控制腳本實現了在內核初始化的過程中通過簡單的迴圈就實現了相關驅動的初始化
也就是說,在linux初始化期間,就已經執行了s3c24xx_gpiolib_init,現在我們可以分析下s3c_gpiolib_add(chip); 這個函數了,

__init void s3c_gpiolib_add(struct s3c_gpio_chip *chip)
{
struct gpio_chip *gc = &chip->chip;
int ret;
BUG_ON(!chip->base);
BUG_ON(!gc->label);
BUG_ON(!gc->ngpio);
spin_lock_init(&chip->lock); // 初始化s3c_gpio_chip的自旋鎖

if (!gc->direction_input)
gc->direction_input = s3c_gpiolib_input; // direction_input 函數指標

if (!gc->direction_output)

gc->direction_output = s3c_gpiolib_output; // direction_output 函數指標

if (!gc->set)

gc->set = s3c_gpiolib_set; // set函數指標

if (!gc->get)

gc->get = s3c_gpiolib_get; // get函數指標
#ifdef CONFIG_PM
if (chip->pm != NULL) {

if (!chip->pm->save

!chip->pm->resume)

printk(KERN_ERR "gpio: %s has missing PM functions\n", gc->label);

} else

printk(KERN_ERR "gpio: %s has no PM function\n", gc->label);

#endif

/* gpiochip_add() prints own failure message on error. */

ret = gpiochip_add(gc);

if (ret >= 0)

s3c_gpiolib_track(chip);

}

gpiochip_add函數分析:

/**

* gpiochip_add() - register a gpio_chip

* @chip: the chip to register, with chip->base initialized

* Context: potentially before irqs or kmalloc will work

*

* Returns a negative errno if the chip can't be registered, such as

* because the chip->base is invalid or already associated with a

* different chip. Otherwise it returns zero as a success code.

*

* When gpiochip_add() is called very early during boot, so that GPIOs

* can be freely used, the chip->dev device must be registered before

* the gpio framework's arch_initcall(). Otherwise sysfs initialization

* for GPIOs will fail rudely.

*

* If chip->base is negative, this requests dynamic assignment of

* a range of valid GPIOs.

*/

int gpiochip_add(struct gpio_chip *chip) // 在gpio_desc[]中分配空間,並鏈結chip結構

{

unsigned long flags;

int status = 0;

unsigned id;

int base = chip->base;

if ((!gpio_is_valid(base)

!gpio_is_valid(base + chip->ngpio - 1))

&& base >= 0) {

status = -EINVAL;

goto fail;

}

spin_lock_irqsave(&gpio_lock, flags); // 上鎖

if (base < 0) {

base = gpiochip_find_base(chip->ngpio); // 這個函數在gpiolib.c中,在gpio_desc[]中分配chip->ngpio個空間(從最後往前分配),返回第一個index

if (base < 0) { // 分配不到

status = base;

goto unlock; // 解鎖退出

}

chip->base = base; // gpio_chip *chip->base = i (i是gpio_desc[i]中的index)

}
/* these GPIO numbers must not be managed by another gpio_chip */

for (id = base; id < base + chip->ngpio; id++) {

if (gpio_desc[id].chip != NULL) {

status = -EBUSY;

break;

}

}

if (status == 0) { // 分配到空間,正常情況下

for (id = base; id < base + chip->ngpio; id++) {

gpio_desc[id].chip = chip; // 這裏將gpio_desc與s3c_gpio_chip聯繫起來,他們的chip成員指向的是同一個資料結構

/* REVISIT: most hardware initializes GPIOs as

* inputs (often with pullups enabled) so power

* usage is minimized. Linux code should set the

* gpio direction first thing; but until it does,

* we may expose the wrong direction in sysfs.

*/

gpio_desc[id].flags = !chip->direction_input ? (1 << FLAG_IS_OUT) : 0; // 設置flags

}

} // end if

of_gpiochip_add(chip); // 沒操作

unlock:

spin_unlock_irqrestore(&gpio_lock, flags); // 解鎖

if (status)

goto fail;

status = gpiochip_export(chip); //×××××××××××××××待分析

if (status)

goto fail;

return 0;

fail:

/* failures here can mean systems won't boot... */

pr_err("gpiochip_add: gpios %d..%d (%s) failed to register\n",

chip->base, chip->base + chip->ngpio - 1,

chip->label ? : "generic");

return status; // 返回狀態

}

下面是s3c_gpiolib_track函數

#ifdef CONFIG_S3C_GPIO_TRACK

struct s3c_gpio_chip *s3c_gpios[S3C_GPIO_END];

static __init void s3c_gpiolib_track(struct s3c_gpio_chip *chip) // 沒完全理解,待分析

{

unsigned int gpn;

int i;

gpn = chip->chip.base;

for (i = 0; i < chip->chip.ngpio; i++, gpn++) {

BUG_ON(gpn >= ARRAY_SIZE(s3c_gpios));

s3c_gpios[gpn] = chip;

}

}

#endif /* CONFIG_S3C_GPIO_TRACK */

***************************************************************************

好,現在我們開始分析設備註冊與卸載函數,在初始化程式中,有如下語句:

ret = misc_register(&misc); //註冊設備

其中的misc_register就是雜項設備的註冊函數,首先關注下這裏的參數misc資料結構

75 /*

76 * 把 LED 驅動註冊為 MISC 設備

77 */

78 static struct miscdevice misc = {

79 .minor = MISC_DYNAMIC_MINOR, //動態設備號

80 .name = DEVICE_NAME,

81 .fops = &dev_fops,

82 };

miscdevice的資料結構如圖三所示:

/**

* misc_register - register a miscellaneous device

* @misc: device structure

*

* Register a miscellaneous device with the kernel. If the minor

* number is set to %MISC_DYNAMIC_MINOR a minor number is assigned

* and placed in the minor field of the structure. For other cases

* the minor number requested is used.

*

* The structure passed is linked into the kernel and may not be

* destroyed until it has been unregistered.

*

* A zero is returned on success and a negative errno code for

* failure.

*/

int misc_register(struct miscdevice * misc)
{
struct miscdevice *c;
dev_t dev;
int err = 0;

INIT_LIST_HEAD(&misc->list); // 初始化鏈表頭,將misc->list的next和pre都指向自己
mutex_lock(&misc_mtx); // 獲取互斥鎖, or睡眠
list_for_each_entry(c, &misc_list, list) { // 遍曆整個misc_list鏈表,所有的雜項驅動設備都有一個miscdevice資料結構,這些雜項驅動設備通過一個全局的misc_list鏈表連在一起, 相當一個記錄

if (c->minor == misc->minor) { // 如果misc_list中已經有了這個設備(minor相同),則解鎖返回,這裏c是遍歷時的tmp miscdevice,指向當前遍曆節點

mutex_unlock(&misc_mtx);

return -EBUSY;

}

}

if (misc->minor == MISC_DYNAMIC_MINOR) { // 如果misc_list中沒有該設備,判斷minor是否準備動態分配,實驗中如此設置

int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS); // misc_minors是雜項設備點陣圖,總共有64個位DYNAMIC_MINORS=64,表示可以註冊64個雜項設備,這句代碼找到點陣圖中的空閒位置(表示還能加新設備)

if (i >= DYNAMIC_MINORS) { // 如果超過總設備數,則解鎖返回

mutex_unlock(&misc_mtx);

return -EBUSY;

}

misc->minor = DYNAMIC_MINORS - i - 1; // 計算子設備號,賦值到misc->minor

set_bit(i, misc_minors); // 對應的點陣圖也置位

}

dev = MKDEV(MISC_MAJOR, misc->minor); // 生成設備號
// 在sysfs中創建並註冊一個設備,可以在/dev下面看到misc->name
misc->this_device = device_create(misc_class, misc->parent, dev, misc, "%s", misc->name);
1480/**
1481 * device_create - creates a device and registers it with sysfs
1482 * @class: pointer to the struct class that this device should be registered to
1483 * @parent: pointer to the parent struct device of this new device, if any
1484 * @devt: the dev_t for the char device to be added
1485 * @drvdata: the data to be added to the device for callbacks
1486 * @fmt: string for the device's name
1487 *
1488 * This function can be used by char device classes. A struct device
1489 * will be created in sysfs, registered to the specified class.
1490 *
1491 * A "dev" file will be created, showing the dev_t for the device, if
1492 * the dev_t is not 0,0.
1493 * If a pointer to a parent struct device is passed in, the newly created
1494 * struct device will be a child of that device in sysfs.
1495 * The pointer to the struct device will be returned from the call.
1496 * Any further sysfs files that might be required can be created using this
1497 * pointer.
1498 *
1499 * Returns &struct device pointer on success, or ERR_PTR() on error.
1500 *
1501 * Note: the struct class passed to this function must have previously
1502 * been created with a call to class_create().
1503 */
1504struct device *device_create(struct class *class, struct device *parent, // 這個函數以後會詳細看
1505 dev_t devt, void *drvdata, const char *fmt, ...)
1506{
1507 va_list vargs;
1508 struct device *dev;
1509
1510 va_start(vargs, fmt);
1511 dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
1512 va_end(vargs);
1513 return dev;
1514}

// this_device是在創建設備節點時指向函數device_create()返回的設備結構
if (IS_ERR(misc->this_device)) { // 如果創建節點出錯,並且
int i = DYNAMIC_MINORS - misc->minor - 1; // 計算子設備號之前misc->minor的值
if (i < DYNAMIC_MINORS && i >= 0) // 計算點陣圖位i,如果在0-64之間,說明在set_bit中置位了,則清楚點陣圖,處理錯誤,準備返回

clear_bit(i, misc_minors);

err = PTR_ERR(misc->this_device);

goto out;

}

/*
* Add it to the front, so that later devices can "override"
* earlier defaults
*/
list_add(&misc->list, &misc_list); // 以上操作都沒有問題後,將新設備加入misc_list鏈表,解鎖返回

out:

mutex_unlock(&misc_mtx);

return err;

}

***************************************************************************
同樣,對應misc_register函數,在exit中會調用misc_deregister函數

/**

* misc_deregister - unregister a miscellaneous device

* @misc: device to unregister

*

* Unregister a miscellaneous device that was previously

* successfully registered with misc_register(). Success

* is indicated by a zero return, a negative errno code

* indicates an error.

*/

int misc_deregister(struct miscdevice *misc)

{

int i = DYNAMIC_MINORS - misc->minor - 1;
if (WARN_ON(list_empty(&misc->list))) // 如果該misc->list的next指向自己,則出錯返回

return -EINVAL;
mutex_lock(&misc_mtx); // 上鎖
list_del(&misc->list); // 將misc從misc_list鏈表中刪除
device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor)); // 對應device_create!
1524/**
1525 * device_destroy - removes a device that was created with device_create()
1526 * @class: pointer to the struct class that this device was registered with
1527 * @devt: the dev_t of the device that was previously registered
1528 *
1529 * This call unregisters and cleans up a device that was created with a
1530 * call to device_create().
1531 */
1532void device_destroy(struct class *class, dev_t devt)
1533{
1534 struct device *dev;
1535
1536 dev = class_find_device(class, NULL, &devt, __match_devt);
1537 if (dev) {
1538 put_device(dev);
1539 device_unregister(dev);
1540 }
1541}
1542EXPORT_SYMBOL_GPL(device_destroy);
if (i < DYNAMIC_MINORS && i >= 0)
clear_bit(i, misc_minors); // 計算點陣圖位i,如果在0-64之間,說明在set_bit中置位了,清楚點陣圖

mutex_unlock(&misc_mtx); // 解鎖返回
return 0;

}

***************************************************************************
總結雜項設備驅動的註冊與卸載流程:
misc_register:找到空閒設備點陣圖位置 -> 計算子設備號(如果動態的話),點陣圖位置位元 - > device_creat() -> miscdevice結構體加入misc_list鏈表中

misc_deregister: 將miscdevice結構體從misc_list鏈表中刪除 -> device_destory() -> 點陣圖位清零

***************************************************************************
與s3c24xx_gpiolib_init函數一樣,misc也有一個初始化函數會在linux初始化時運行,下面來分析這個函數
static int __init misc_init(void)
{
int err;
#ifdef CONFIG_PROC_FS //在proc檔系統下創建一個"misc"目錄。 misc_proc_fops是該檔系統下檔的操作函數集
proc_create("misc", 0, NULL, &misc_proc_fops);
#endif
misc_class = class_create(THIS_MODULE, "misc"); // 前面device_create()中的misc_class就是在這裏初始化的
err = PTR_ERR(misc_class);
if (IS_ERR(misc_class)) // 出錯處理
goto fail_remove;
err = -EIO;
if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) //註冊一個主設備號為MISC_MAJOR(10)的字元設備,設備操作函數集為misc_fops
goto fail_printk;
misc_class->devnode = misc_devnode;
return 0;
fail_printk: // 錯誤處理
printk("unable to get major %d for misc devices\n", MISC_MAJOR);
class_destroy(misc_class);
fail_remove:
remove_proc_entry("misc", NULL);
return err;
}

subsys_initcall(misc_init);

***************************************************************************
好,到這裏基本把一些GPIO相關的基本函數和結構體都簡單說明了,雖然還有不少不清楚的地方,但還是有些幫助,文中還有些不清楚的地方還有待以後能一一解決,我會不斷補充!
--> 閱讀更多...

2011年6月17日 星期五

Cortex-M3在UCOS2實例演練

目前先將圖片貼上來;爾後再慢慢說明.
實作板子上的週邊功能主畫面

介紹畫面(並加入big5繁體顯示功能)


ADC範例

讀寫sram

實作rtc實鐘(GUI)

UCOS2開始畫面


--> 閱讀更多...

2010年12月21日 星期二

認識Cortex-M3 ARM MCU

前不久買了這塊開發板,配了一片dvd,裡頭資料超多的;心想這麼多的specification要K到何時。而且這顆MCU雖然在速度上只有ARM9的4分之1不到,但畢竟產品定位不同(它其實是arm7的後繼版本)。然而它內建周邊卻比ARM9強很多;由於這些板子都是對岸的同胞們所製作的產品在價格上都非常有親合力,若想真正進入arm這個領域,現在這一款Cortex-M3是很棒的入門版。別以為入門兩個字就把它想的很簡單,因為其內建周邊非常多,功能性及可作的運用也很多,很多應用領域的controller應該也都是使用這類mcu。若是你想直接進入linux嵌入式系統,那麼可能要選擇含有mmu的Cortex-A8的開發板,當然選擇arm 9及11也是可以,否則只能選擇uclinux (for none MMU)。
由於這麼多的週邊設置,因此已經沒有人會想以整個專案都採用組合語言來開發(非常吃力又難維護)。所以ST公司便針對STM32F10XXX發行了韌體函式庫(大陸那邊稱為固件庫)。所以專案可以採用C來開發。其實說的好像很簡單,但是面對一個新的架構怎麼可能會很快上手呢?除了K SPEC以外就是要不斷的實作;然後再K SPEC。為何會寫這一篇文章,主要就是告訴自己警惕自己,學習這塊板子沒有捷徑;即使K的很痛苦,但是值得。
在此提供一pdf教學檔:Cortex-M3權威指南CnR2(電子書).pdf 。
--> 閱讀更多...

2010年7月6日 星期二

u-boot 顯示logo,並說明其原理part1


jpegtopnm Benson.jpg l ppmquant 31 l ppmtobmp -bpp 8 > Benson-8bit.bmp
紅色l是管線符號,因無法正常顯示所以用小寫L取代
以上這個指令必須先安裝netpbm,其作用是將jpeg檔轉成特定格式的bmp檔。將Benson-8bit.bmp更改檔名為denx.bmp並存到tools\logos,重新compile u-boot,便可完成,如上圖。接著我們來說明其draw logo原理:
在/tools/Makefile:
LOGO_H = $(OBJTREE)/include/bmp_logo.h
及
ifeq ($(LOGO_BMP),)
LOGO_BMP= logos/denx.bmp
endif
ifeq ($(VENDOR),atmel)
LOGO_BMP= logos/atmel.bmp
endif
及
$(LOGO_H):$(obj)bmp_logo $(LOGO_BMP)
$(obj)./bmp_logo $(LOGO_BMP) >$@ #這個$@就是bmp_logo.h
也就是說$(LOGO_H)這個目標的必要條件是bmp_logo 及denx.bmp這兩個檔案,才能產生bmp_logo.h這個主角,它就是denx.bmp的位元圖陣列。
所以在uboot主目錄中的makefile中有這麼一行:@rm -f $(obj)include/bmp_logo.h。在clean 時會將其刪除。
以上資訊若使用Source Insight根本無法搜尋得到,終就還是得到linux使用gerp。像這次我的搜尋指令如下:grep -Hwn "bmp_logo.h" -r ./ 。接著我們來了解一下bmp_logo.c:這是一支單一檔案所完成的獨立執行檔;我先把代碼列出;再來加入註解;另外也必須了解點陣圖的格式,請參考點陣圖(Bitmap)檔案格式
#include (stdio.h)
#include (stdlib.h)
#if defined(__linux__)
#include (stdint.h)
#else
#ifdef __CYGWIN__
#include "elf.h"
#else
#include (inttypes.h)
#endif
#endif

typedef struct bitmap_s { /* bitmap description */
uint16_t width;
uint16_t height;
uint8_t palette[256*3]; //調色盤資料
uint8_t *data; //圖的bit_map資料指標
} bitmap_t;

#define DEFAULT_CMAP_SIZE 16 /* size of default color map */

/* Neutralize little endians. */
uint16_t le_short(uint16_t x) //因為檔案格式是依照LITTLE ENDIAN排列,因此要找出實際數值必須作移位。
{
uint16_t val;
uint8_t *p = (uint8_t *)(&x);
val = (*p++ & 0xff) 左移 0;
val = (*p & 0xff) 左移 8;
return val;
}
void skip_bytes (FILE *fp, int n)
{
while (n-- > 0) fgetc (fp);
}

int main (int argc, char *argv[])
{
int i, x;
FILE *fp;
bitmap_t bmp;
bitmap_t *b = &bmp;
uint16_t data_offset, n_colors;

if (argc < color="#cc33cc">//判斷參數少於2便顯示fail
fprintf (stderr, "Usage: %s file\n", argv[0]);
exit (EXIT_FAILURE);
}
if ((fp = fopen (argv[1], "rb")) == NULL) {//開啟檔案,若失敗顯示fail
perror (argv[1]);
exit (EXIT_FAILURE);
}
if (fgetc (fp) != 'B' fgetc (fp) != 'M') { //判斷檔頭前2個byte是否為"BM",否則顯示錯誤
fprintf (stderr, "%s is not a bitmap file.\n", argv[1]);
exit (EXIT_FAILURE);
}
/*
* read width and height of the image, and the number of colors used;
* ignore the rest
*/
skip_bytes (fp, 8);
fread (&data_offset, sizeof (uint16_t), 1, fp);
skip_bytes (fp, 6);
fread (&b->width, sizeof (uint16_t), 1, fp);
skip_bytes (fp, 2);
fread (&b->height, sizeof (uint16_t), 1, fp);
skip_bytes (fp, 22);
fread (&n_colors, sizeof (uint16_t), 1, fp);
skip_bytes (fp, 6);
/*
* Repair endianess. 求出實際數值,包括data_offset b->width b->height n_colors
*/
data_offset = le_short(data_offset);
b->width = le_short(b->width);
b->height = le_short(b->height);
n_colors = le_short(n_colors);

/* assume we are working with an 8-bit file */
if ((n_colors == 0) or (n_colors > 256 - DEFAULT_CMAP_SIZE)) {
/* reserve DEFAULT_CMAP_SIZE color map entries for default map */
n_colors = 256 - DEFAULT_CMAP_SIZE;
}
//使用printf便可印到bmp_logo.h那是因為$(obj)./bmp_logo $(LOGO_BMP) >$@ 。 這個">"轉向指令。
以下的代碼就不用再加註解,只要比對一下產生出來的bmp_logo.h,就知道了
printf ("/*\n"
" * Automatically generated by \"tools/bmp_logo\"\n"
" *\n"
" * DO NOT EDIT\n"
" *\n"
" */\n\n\n"
"#ifndef __BMP_LOGO_H__\n"
"#define __BMP_LOGO_H__\n\n"
"#define BMP_LOGO_WIDTH\t\t%d\n"
"#define BMP_LOGO_HEIGHT\t\t%d\n"
"#define BMP_LOGO_COLORS\t\t%d\n"
"#define BMP_LOGO_OFFSET\t\t%d\n"
"\n",
b->width, b->height, n_colors,
DEFAULT_CMAP_SIZE);

/* allocate memory */
if ((b->data = (uint8_t *)malloc(b->width * b->height)) == NULL) {
fclose (fp);
printf ("Error allocating memory for file %s.\n", argv[1]);
exit (EXIT_FAILURE);
}

/* read and print the palette information */
printf ("unsigned short bmp_logo_palette[] = {\n");

for (i=0; ipalette[(int)(i*3+2)] = fgetc(fp);
b->palette[(int)(i*3+1)] = fgetc(fp);
b->palette[(int)(i*3+0)] = fgetc(fp);
x=fgetc(fp);

printf ("%s0x0%X%X%X,%s",
((i%8) == 0) ? "\t" : " ",
(b->palette[(int)(i*3+0)] >> 4) & 0x0F,
(b->palette[(int)(i*3+1)] >> 4) & 0x0F,
(b->palette[(int)(i*3+2)] >> 4) & 0x0F,
((i%8) == 7) ? "\n" : ""
);
}

/* seek to offset indicated by file header */
fseek(fp, (long)data_offset, SEEK_SET);

/* read the bitmap; leave room for default color map */
printf ("\n");
printf ("};\n");
printf ("\n");
printf ("unsigned char bmp_logo_bitmap[] = {\n");
for (i=(b->height-1)*b->width; i>=0; i-=b->width) {
for (x = 0; x <>width; x++) {
b->data[(uint16_t) i + x] = (uint8_t) fgetc (fp) + DEFAULT_CMAP_SIZE;
}
}
fclose (fp);

for (i=0; i<(b->height*b->width); ++i) {
if ((i%8) == 0)
putchar ('\t');
printf ("0x%02X,%c", b->data[i], ((i%8) == 7) ? '\n' : ' ');
}
printf ("\n"
"};\n\n"
"#endif /* __BMP_LOGO_H__ */\n"
);

return (0);
}
--> 閱讀更多...

2010年6月16日 星期三

從start_armboot routine來了解u-boot--PART2

接續上回談到env_init,列出相關代碼:注意,這裡的代碼分散在不同的檔案
typedef struct environment_s {
uint32_t crc; /* CRC32 over data bytes */
#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
unsigned char flags; /* active/obsolete flags */
#endif
unsigned char data[ENV_SIZE]; /* Environment data */
} env_t;


#define ENV_SIZE (CONFIG_ENV_SIZE - ENV_HEADER_SIZE)
#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
# define ENV_HEADER_SIZE (sizeof(uint32_t) + 1)
#else
# define ENV_HEADER_SIZE (sizeof(uint32_t))
#endif


#ifdef ENV_IS_EMBEDDED //這個marco未定義
extern uchar environment[];
env_t *env_ptr = (env_t *)(&environment[0]); //因此以上兩行未執行
#else /* ! ENV_IS_EMBEDDED */
env_t *env_ptr = 0;
#endif /* ENV_IS_EMBEDDED */


#define local static
#define ZEXPORT


#ifdef DYNAMIC_CRC_TABLE //在u-boot的code裡並未定義這個MARCO;所以並不會動態產生CRC_TABLE
local int crc_table_empty = 1;
local uint32_t crc_table[256];
local void make_crc_table OF((void));


local void make_crc_table()
{
uint32_t c;
int n, k;
uLong poly; /* polynomial exclusive-or pattern */
/* terms of polynomial defining this crc (except x^32): */
static const Byte p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};

/* make exclusive-or pattern from polynomial (0xedb88320L) */
poly = 0L;
for (n = 0; n < n =" 0;" c =" (uLong)n;" k =" 0;" c =" c">> 1) : c >> 1;
crc_table[n] = c;
}
crc_table_empty = 0;
}
#else
/* ========================================================================
* Table of CRC-32's of all single-byte values (made by make_crc_table)
*/
local const uint32_t crc_table[256] = { //這個crc_table會被配置
0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
0x2d02ef8dL
};
#endif /*DYNAMIC_CRC_TABLE */


#ifndef OF /* function prototypes */
# ifdef STDC
# define OF(args) args
# else
# define OF(args) ()
# endif
#endif


#define DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8);
#define DO2(buf) DO1(buf); DO1(buf);
#define DO4(buf) DO2(buf); DO2(buf);
#define DO8(buf) DO4(buf); DO4(buf);

//以下這個routine在lib_generic/crc32.c

uint32_t ZEXPORT crc32 (uint32_t crc, const Bytef *buf, uInt len)
{
#ifdef DYNAMIC_CRC_TABLE
if (crc_table_empty) make_crc_table(); //這行不會被執行
#endif
crc = crc ^ 0xffffffffL;
while (len >= 8)
{
DO8(buf);
len -= 8;
}
if (len) do {
DO1(buf);
} while (--len);
return crc ^ 0xffffffffL;
}


先說明關於CRC,以CRC-16為例來說明其生成過程:
CRC-16碼由兩個位元組構成,在開始時CRC寄存器的每一位都預置為1,然後把CRC寄存器與8-bit的資料進行異或(異或:二進位運算相同為0,不同為1;0^0=0;0^1=1;1^0=1;1^1=0),之後對CRC寄存器從高到低進行移位,在最高位(MSB)的位置補零,而最低位(LSB,移位後已經被移出CRC寄存器)如果為1,則把寄存器與預定義的多項式碼進行異或,否則如果LSB為零,則無需進行異或。重複上述的由高至低的移位8次,第一個8-bit資料處理完畢,用此時CRC寄存器的值與下一個8-bit資料異或並進行如前一個資料似的8次移位元。所有的字元處理完成後CRC寄存器內的值即為最終的CRC值。
下面為CRC的計算過程:
来源:(http://blog.sina.com.cn/s/blog_639260ff0100g1ms.html) - 循环冗余校验CRC_Striker_新 1.設置CRC寄存器,並給其賦值FFFF(hex)。
2.將資料的第一個8-bit字元與16位元CRC寄存器的低8位進行異或,並把結果存入CRC寄存器。
3.CRC寄存器向右移一位,MSB補零,移出並檢查LSB。
4.如果LSB為0,重複第三步;若LSB為1,CRC寄存器與多項式碼相異或。
5.重複第3與第4步直到8次移位元全部完成。此時一個8-bit資料處理完畢。
6.重複第2至第5步直到所有資料全部處理完成。
7.最終CRC寄存器的內容即為CRC值。
常用的CRC迴圈冗餘校驗標準多項式如下:
CRC(16位) = X16+X15+X2+1
CRC(CCITT) = X16+X12 +X5+1
CRC(32位) = X32+X26+X23+X16+X12+X11+X10+ X8+X7+X5+X4+X2+X+1

--> 閱讀更多...

2010年6月14日 星期一

從start_armboot routine來了解u-boot--PART1

有關這方面ㄉ相關資訊參考:http://blog.chinaunix.net/u2/70445/showart_1852111.html
http://6xudonghai.blog.163.com/blog/static/33640629200911364735696/
http://www.63da.com/?All1-uboot%D4%B4%C2%EB%B7%D6%CE%F6/
(1):typedef int (init_fnc_t) (void);
(2):init_fnc_t *init_sequence[] ={
cpu_init, /* basic cpu dependent setup */ (1)
board_init, /* basic board dependent setup */(2)
interrupt_init, /* set up exceptions */ (3)
env_init, /* initialize environment */ (4)
init_baudrate, /* initialze baudrate settings */(5)
serial_init, /* serial communications setup */ (6)
console_init_f, /* stage 1 init of console */ (7)
display_banner, /* say that we are here */ (8)
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo, /* display cpu info (and speed) */(9)
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
checkboard, /* display board info */ (10)
#endif
#if defined(CONFIG_HARD_I2C) defined(CONFIG_SOFT_I2C)
init_func_i2c, (11)
#endif
dram_init, /* configure available RAM banks */ (12)
display_dram_config, (13)
NULL,
};

(3):init_fnc_t **init_fnc_ptr;

(4)for (init_fnc_ptr = init_sequence ; *init_fnc_ptr ; ++init_fnc_ptr){
if ((*init_fnc_ptr)() != 0)
{hang (); }
}
以上四段code分散在board.c中,但是它們息息相關,而最下面的for迴圈更是高竿的程式寫法:以下我們就先針對這四段code了解其如何對硬體層面作完整的初始化。
我想大家對c語言有了解的應該都知道int main(int argc,char**argv){ };其實也可以這樣寫int main(int argc,char*argv[]){ };當然以下這樣也行int main(int argc,char argv[][]){ }; 所以上面的code不言自明,說穿了也是函式指標的應用。
剛才講到for迴圈,它主要的工作是硬體週邊初始化;和硬體週邊相關的代碼必需查datasheet,才能了解其實作,前三個:cpu_init、board_init、interrupt_init我認為較簡單,而且代碼上都有註解。其他部份我想未來有談到再補充。
在此我認為較難懂的是env_init,因為實際上在u-boot中這個routine就有9個之多,所以我在上面貼了一個參考連結,專門講述這個routine。雖然裡頭並未說明---實際呼叫的是env_nand.c或是env_dataflash.c或是env_flash.c...中的env_init();實際查看在common/makefile如下:
# environment
COBJS-y += env_common.o
COBJS-$(CONFIG_ENV_IS_IN_DATAFLASH) += env_dataflash.o
COBJS-$(CONFIG_ENV_IS_IN_EEPROM) += env_eeprom.o
COBJS-y += env_embedded.o
COBJS-$(CONFIG_ENV_IS_IN_FLASH) += env_flash.o
COBJS-$(CONFIG_ENV_IS_IN_NAND) += env_nand.o
COBJS-$(CONFIG_ENV_IS_IN_NVRAM) += env_nvram.o
COBJS-$(CONFIG_ENV_IS_IN_ONENAND) += env_onenand.o
COBJS-$(CONFIG_ENV_IS_IN_SPI_FLASH) += env_sf.o
COBJS-$(CONFIG_ENV_IS_NOWHERE) += env_nowhere.o
然後再查看include\configs\smdk2410.h如下:
#define CONFIG_ENV_IS_IN_FLASH 1
#define CONFIG_ENV_SIZE 0x10000 /* Total Size of Environment Sector */
●所以在此會被編譯的只有env_flash.c。
然而在tekkaman所改版的部分它將smdk2410.h重新命名為mini2440.h,並修改其中內容如下:
//#define CONFIG_ENV_IS_IN_EEPROM 1/* use EEPROM for environment vars */
//#define CONFIG_ENV_OFFSET 0x000 /* environment starts at offset 0 */
//#define CONFIG_ENV_SIZE 0x400 /* 1KB */
#define CONFIG_ENV_IS_IN_NAND 1
//#define CONFIG_ENV_IS_IN_FLASH 1 //注意此行被mark掉了
#define CONFIG_ENV_OFFSET 0X60000
#define CONFIG_ENV_SIZE 0x10000 /* Total Size of Environment Sector */
所以在此會被編譯的是env_nand.c。

另外題外話:最近使用arm-linux-objdump -D u-boot >u-boot.asm這個指令將u-boot反組譯後有助於trace參考之方便性。
--> 閱讀更多...

2010年6月10日 星期四

S3C2440與NAND FLASHK9F1208的接線分析

圖一
圖二
圖三
圖四
圖五
本文來自: 高校自動化網(http://www.zdh1909.com/) 詳細出處參考(轉載請保留本鏈結):http://www.zdh1909.com/html/MCS51/8883.html
NAND FLASH的接線方式和NOR FLASH,SDRAM都不一樣。以TQ2440開發板用的K9F1208為例,分析NAND FLASH的接線方式。K9F1208結構如圖一所示:
K9F1208位寬是8bit。
一頁: 512byte + 16byte 最後16byte是用於存儲校驗碼和其他資訊用的,不能存放實際的資料。
一個塊有32 page:(16k+512)byte
K9F1208有4096個塊:(64M+2M)byte,總共有64Mbyte可操作的晶片容量
NAND FLASH以頁為單位讀寫資料,以塊為單位擦除數據。
S3C24440和K9F1208的接線如圖二:
圖三是S3C2440的NAND FLASH引腳配置:
當選定一個NAND FLASH的型號後,要根據選定的NAND FLASH來確定S3C2440的NCON,GPG13,GPG14,GPG15的狀態。
圖四是S3C2440中4個腳位元狀態的定義:
K9F1208的一頁是512byte,所以NCON接低電平,GPG13接高電平。
K9F1208需要4個定址命令,所以GPG14接高電平K9F1208的位寬是8,所以GPG15接低電平。
NAND FLASH定址對K9F1208來說,地址和命令只能在I/O[7:0]上傳遞,資料寬度是8位元。
地址傳遞分為4步,如圖五
:
第1步發送列位址,既選中一頁512BYTE中的一個位元組。512byte需要9bit來選擇,這裏只用了A0-A7,原因是把一頁分成了2部分,每部分256位元組。通過發送的讀命令字來確定是讀的前256位元組還是後256位元組。
當要讀取的起始地址(Column Address)在0~255內時我們用00h命令,當讀取的起始位址是在256~511時,則使用01h命令。
一個塊有32頁,用A9-A13共5位來選擇一個塊中的某個頁。
總共有4096個塊,用A14-A25共12位來選擇一個塊。
K9F1208總共有64Mbyte,需要A0-A25共26個位址位。
例如要讀NAND FLASH的第5000位元組開始的內容。把5000分解成列地址和行地址。
Column_address = 5000%512 = 392
Page_address = (5000>>9) = 9
因為column_address>255,所以用01h命令讀
發送命令和參數的順序是:
NFCMMD = 0x01;從後256位元組開始讀
NFADDR = column_address & 0xff;取column_address的低8位元送到數據線
NFADDR = page_address & 0xff;發送A9-A16
NFADDR = (page_address >>8) & 0xff; 發送A17-A24
NFADDR = (page_address >> 16) & 0xff;發送A25
上面的NFCMMD,NFADDR.是S3C2440的NAND FLASH控制寄存器。讀取的數據會放在NFDATA中。
--> 閱讀更多...

2010年4月28日 星期三

2440init.s 2440slib.s說明

2440init.s 2440slib.s這兩個檔案相當於是在ADS1.2開發環境中的簡易bootloader,其實只要輸入這兩個檔名就可搜尋到相關的中文註解說明例如S3C2440 2440init.s分析 2440启动代码注解 ;由於它的組合語言語法與GNU上的組合語言雖然一樣,但是卻有一些假指令,因此我把假指令的相關說明貼於此:arm偽指令 ARM汇编伪指令介绍。
其實在2440init.s中很多代碼是可以拿掉的;特別是那些以左右方括號[ ]作為以if endif,以l做為else的那些判斷式,原因可參考上面連結的相關說明。
今天我們其實重點是擺在2440slib.s這個檔案;2440slib.s注解,然而我個人覺得這個註解寫的並不很清楚,因此我打算自己來搞一遍。
這個檔裡頭都定義了一些和MMU相關聯的底層Routine:例如MMU_EnableICache MMU_DisableICache MMU_EnableDCache MMU_EnableMMU‧‧這裡也列出一些相關連結說明:ARM处理器架构-内存映射 内存管理单元(MMU)介绍 、 FS2410 开发板上启用 MMU 实现虚拟内存管理 、 内存管理单元(MMU)和协处理器CP15介绍 、 s3c2410 MMU(存储器管理单元)讲解 、 ARM920T关闭MMU,cache以及写缓冲区,CP15详解 、 ARM920T的MMU与Cache之操作MMU和Cache的内核启动代码 、 嵌入式Linux学习笔记(四)-内存管理单元mmu。内核关键链接脚本。
在linux kernel有關mmu設定請參考linux/arch/arm/boot/compressed/head.S
對以上的連結看過後就知道其實就是在搞cp15協同處理器;2440slib.s代碼實際內容如下:
目前我只把code擺上來,未來會逐一update
;==========================================
; File Name : 2440slib.s
; Function : S3C2440 (Assembly)
; Date : March 09, 2002
; Revision : Programming start (February 26,2002) -> SOP
; Revision : 03.11.2003 ver 0.0 Attatched for 2440
;==========================================
;Interrupt, FIQ/IRQ disable
NOINT EQU 0xc0 ; 1100 0000
;Check if tasm.exe(armasm -16 ...@ADS 1.0) is used.
GBLL THUMBCODE ;宣告一個全域邏輯變數THUMBCODE 預設值為FALSE
[ {CONFIG} = 16 ;如果CONFIG=16
THUMBCODE SETL {TRUE} ;設定THUMBCODE為TRUE
CODE32 ;
假指令CODE32,表示以下的代碼段為ARM 32bit 編碼
l ;else
THUMBCODE SETL {FALSE} ; 設定THUMBCODE為FALSE
] ;
endif

MACRO ;巨集宣告
MOV_PC_LR ;巨集名稱
[ THUMBCODE ;如果THUMBCODE為TRUE,則執行下一行,否則跳到下二行else處
bx lr ;
同call r14
l ;else
mov pc,lr ;r15<-r14
] ;endif
MEND ;巨集結尾

AREA C$$code, CODE, READONLY ;段宣告 段名稱C$$code 段屬性為代碼段唯讀
EXPORT EnterCritical
EnterCritical
mrs r1, cpsr
str r1, [r0]
orr r1, r1, #NOINT
msr cpsr_cxsf, r1
MOV_PC_LR
;restore cpsr, r0 = address to restore cpsr
EXPORT ExitCritical
ExitCritical
ldr r1, [r0]
msr cpsr_cxsf, r1
MOV_PC_LR
;==============
; CPSR I,F bit
;==============
;int SET_IF(void);
;The return value is current CPSR.
EXPORT SET_IF
SET_IF
;This function works only if the processor is in previliged mode.
mrs r0,cpsr
mov r1,r0
orr r1,r1,#NOINT
msr cpsr_cxsf,r1
MOV_PC_LR

;void WR_IF(int cpsrValue);
EXPORT WR_IF
WR_IF
;This function works only if the processor is in previliged mode.
msr cpsr_cxsf,r0
MOV_PC_LR


;void CLR_IF(void);
EXPORT CLR_IF
CLR_IF
;This function works only if the processor is in previliged mode.
mrs r0,cpsr
bic r0,r0,#NOINT
msr cpsr_cxsf,r0
MOV_PC_LR

EXPORT outportw
outportw strh r0, [r1]
MOV_PC_LR

EXPORT inportw
inportw ldrh r0, [r0]
MOV_PC_LR


;====================================
; MMU Cache/TLB/etc on/off functions
;====================================
R1_I EQU (1<<12)>EXPORT MMU_EnableICache
MMU_EnableICache
mrc p15,0,r0,c1,c0,0
orr r0,r0,#R1_I
mcr p15,0,r0,c1,c0,0
MOV_PC_LR

;void MMU_DisableICache(void)
EXPORT MMU_DisableICache
MMU_DisableICache
mrc p15,0,r0,c1,c0,0
bic r0,r0,#R1_I
mcr p15,0,r0,c1,c0,0
MOV_PC_LR

;void MMU_EnableDCache(void)
EXPORT MMU_EnableDCache
MMU_EnableDCache
mrc p15,0,r0,c1,c0,0
orr r0,r0,#R1_C
mcr p15,0,r0,c1,c0,0
MOV_PC_LR

;void MMU_DisableDCache(void)
EXPORT MMU_DisableDCache
MMU_DisableDCache
mrc p15,0,r0,c1,c0,0
bic r0,r0,#R1_C
mcr p15,0,r0,c1,c0,0
MOV_PC_LR

;void MMU_EnableAlignFault(void)
EXPORT MMU_EnableAlignFault
MMU_EnableAlignFault
mrc p15,0,r0,c1,c0,0
orr r0,r0,#R1_A
mcr p15,0,r0,c1,c0,0
MOV_PC_LR

;void MMU_DisableAlignFault(void)
EXPORT MMU_DisableAlignFault
MMU_DisableAlignFault
mrc p15,0,r0,c1,c0,0
bic r0,r0,#R1_A
mcr p15,0,r0,c1,c0,0
MOV_PC_LR

;void MMU_EnableMMU(void)
EXPORT MMU_EnableMMU
MMU_EnableMMU
mrc p15,0,r0,c1,c0,0
orr r0,r0,#R1_M
mcr p15,0,r0,c1,c0,0
MOV_PC_LR

;void MMU_DisableMMU(void)
EXPORT MMU_DisableMMU
MMU_DisableMMU
mrc p15,0,r0,c1,c0,0
bic r0,r0,#R1_M
mcr p15,0,r0,c1,c0,0
MOV_PC_LR

;void MMU_SetFastBusMode(void)
; FCLK:HCLK= 1:1
EXPORT MMU_SetFastBusMode
MMU_SetFastBusMode
mrc p15,0,r0,c1,c0,0
bic r0,r0,#R1_iA:OR:R1_nF
mcr p15,0,r0,c1,c0,0
MOV_PC_LR

;void MMU_SetAsyncBusMode(void)
; FCLK:HCLK= 1:2
EXPORT MMU_SetAsyncBusMode
MMU_SetAsyncBusMode
mrc p15,0,r0,c1,c0,0
orr r0,r0,#R1_nF:OR:R1_iA
mcr p15,0,r0,c1,c0,0
MOV_PC_LR

;=========================
; Set TTBase
;=========================
;void MMU_SetTTBase(int base)
EXPORT MMU_SetTTBase
MMU_SetTTBase
;ro=TTBase
mcr p15,0,r0,c2,c0,0
MOV_PC_LR

;=========================
; Set Domain
;=========================
;void MMU_SetDomain(int domain)
EXPORT MMU_SetDomain
MMU_SetDomain
;ro=domain
mcr p15,0,r0,c3,c0,0
MOV_PC_LR

;=========================
; ICache/DCache functions
;=========================
;void MMU_InvalidateIDCache(void)
EXPORT MMU_InvalidateIDCache
MMU_InvalidateIDCache
mcr p15,0,r0,c7,c7,0
MOV_PC_LR

;void MMU_InvalidateICache(void)
EXPORT MMU_InvalidateICache
MMU_InvalidateICache
mcr p15,0,r0,c7,c5,0
MOV_PC_LR

;void MMU_InvalidateICacheMVA(U32 mva)
EXPORT MMU_InvalidateICacheMVA
MMU_InvalidateICacheMVA
;r0=mva
mcr p15,0,r0,c7,c5,1
MOV_PC_LR

;void MMU_PrefetchICacheMVA(U32 mva)
EXPORT MMU_PrefetchICacheMVA
MMU_PrefetchICacheMVA
;r0=mva
mcr p15,0,r0,c7,c13,1
MOV_PC_LR

;void MMU_InvalidateDCache(void)
EXPORT MMU_InvalidateDCache
MMU_InvalidateDCache
mcr p15,0,r0,c7,c6,0
MOV_PC_LR

;void MMU_InvalidateDCacheMVA(U32 mva)
EXPORT MMU_InvalidateDCacheMVA
MMU_InvalidateDCacheMVA
;r0=mva
mcr p15,0,r0,c7,c6,1
MOV_PC_LR

;void MMU_CleanDCacheMVA(U32 mva)
EXPORT MMU_CleanDCacheMVA
MMU_CleanDCacheMVA
;r0=mva
mcr p15,0,r0,c7,c10,1
MOV_PC_LR

;void MMU_CleanInvalidateDCacheMVA(U32 mva)
EXPORT MMU_CleanInvalidateDCacheMVA
MMU_CleanInvalidateDCacheMVA
;r0=mva
mcr p15,0,r0,c7,c14,1
MOV_PC_LR

;void MMU_CleanDCacheIndex(U32 index)
EXPORT MMU_CleanDCacheIndex
MMU_CleanDCacheIndex
;r0=index
mcr p15,0,r0,c7,c10,2
MOV_PC_LR

;void MMU_CleanInvalidateDCacheIndex(U32 index)
EXPORT MMU_CleanInvalidateDCacheIndex
MMU_CleanInvalidateDCacheIndex
;r0=index
mcr p15,0,r0,c7,c14,2
MOV_PC_LR

;void MMU_WaitForInterrupt(void)
EXPORT MMU_WaitForInterrupt
MMU_WaitForInterrupt
mcr p15,0,r0,c7,c0,4
MOV_PC_LR

;===============
; TLB functions
;===============
;voic MMU_InvalidateTLB(void)
EXPORT MMU_InvalidateTLB
MMU_InvalidateTLB
mcr p15,0,r0,c8,c7,0
MOV_PC_LR

;void MMU_InvalidateITLB(void)
EXPORT MMU_InvalidateITLB
MMU_InvalidateITLB
mcr p15,0,r0,c8,c5,0
MOV_PC_LR

;void MMU_InvalidateITLBMVA(U32 mva)
EXPORT MMU_InvalidateITLBMVA
MMU_InvalidateITLBMVA
;ro=mva
mcr p15,0,r0,c8,c5,1
MOV_PC_LR

;void MMU_InvalidateDTLB(void)
EXPORT MMU_InvalidateDTLB
MMU_InvalidateDTLB
mcr p15,0,r0,c8,c6,0
MOV_PC_LR

;void MMU_InvalidateDTLBMVA(U32 mva)
EXPORT MMU_InvalidateDTLBMVA
MMU_InvalidateDTLBMVA
;r0=mva
mcr p15,0,r0,c8,c6,1
MOV_PC_LR

;=================
; Cache lock down
;=================
;void MMU_SetDCacheLockdownBase(U32 base)
EXPORT MMU_SetDCacheLockdownBase
MMU_SetDCacheLockdownBase
;r0= victim & lockdown base
mcr p15,0,r0,c9,c0,0
MOV_PC_LR

;void MMU_SetICacheLockdownBase(U32 base)
EXPORT MMU_SetICacheLockdownBase
MMU_SetICacheLockdownBase
;r0= victim & lockdown base
mcr p15,0,r0,c9,c0,1
MOV_PC_LR

;=================
; TLB lock down
;=================
;void MMU_SetDTLBLockdown(U32 baseVictim)
EXPORT MMU_SetDTLBLockdown
MMU_SetDTLBLockdown
;r0= baseVictim
mcr p15,0,r0,c10,c0,0
MOV_PC_LR

;void MMU_SetITLBLockdown(U32 baseVictim)
EXPORT MMU_SetITLBLockdown
MMU_SetITLBLockdown
;r0= baseVictim
mcr p15,0,r0,c10,c0,1
MOV_PC_LR

;============
; Process ID
;============
;void MMU_SetProcessId(U32 pid)
EXPORT MMU_SetProcessId
MMU_SetProcessId
;r0= pid
mcr p15,0,r0,c13,c0,0
MOV_PC_LR

END
--> 閱讀更多...

2010年3月3日 星期三

學習u-boot

http://blog.chinaunix.net/u1/34474/showart.php?id=1877493
http://blog.chinaunix.net/u2/75270/showart_1779196.html
http://blog.chinaunix.net/u3/98913/article.html
這兩個連結算是我最近search到最棒的u-boot介紹:
以下內容便是轉載於第二個連結,由於這資料非常寶貴,因此我當然要post一份到自己的blog;以免萬一原文超連結遺失。
u-boot2008.10非nand_leagcy移植mini2440,支持yaffs,附源碼
這篇文章寫於2008.12.28日,主要記錄了我移植u-boot-2008.10的過程,並附上了移植好的patch檔。移植好的u-boot-2008.10適用友善公司的mini2440和陽初公司的yc2410。其他的開發板,可能要根據相應的電路配置做稍許修改。我的移植是使用非nand-leagcy方法的,移植好的u-boot-2008.10功能除了基本功能外,加上了yaffs1映射的寫入功能,加入了從nand flash啟動的功能,改善了一些操作感受,往nand寫入資料時,可以顯示進度。在使用上要注意的是使用nand wirte.yaffs 命令寫入yaffs1映射時,文件長度參數一定要與yaffs1映射的大小完全一致,否則有可能產生假壞塊。我的開發環境是vmware,kubuntu8.04。交叉編譯是用crosstool0.43編譯生成的arm-linux-gcc 4.1.0,libc 2.3.2。
(本人也成功移植了linux2.6.27.9到mini2440開發板上,有需要源碼的,可以和我聯繫。EMAIL:nanjinrat@sohu.com,映射文件在此下載http://blog.chinaunix.net/u2/75270/showart.php?id=1796658 映射的使用方法見我的另一篇文章。http://blog.chinaunix.net/u2/75270/showart.php?id=1836713)一直想自已移植一套u-boot,但因為工作忙,一直都沒有做,最近時間比較多,買了一套友善之臂的mini2440開發板,此板電路與該公司之前的QQ2440基本一至。而該開發板自帶的u-boot的移植的還不很完善,於是下決心自已移植u-boot。要移植就用最新版的u-boot移植,於是決定在u-boot.2008.10版上進行移植,此版是2008年10月的新版,於之前的版本有較大改動,所以版本號也沒有延續以前的編號方式,而改為2008.10。我的移植目的,是要能同時持S3C2440,S3C2410(手中還有一塊陽初公司出的S3C2410開發板),從nand flash啟動u-boot,因為目前大多數應用都是只有nand flash的,所以沒打算從nor flash啟動。支援tftp的使用,也就說要移植網卡的驅動,mini2440和陽初的s3c2410自帶的linux都是使用yaffs檔系統作為根檔系統的,因此,u-boot還要能支援yaffs映射的燒寫。在此我將移植過程記錄下來,以方便大家在移植此版u-boot時參考。為了方便整個移植過程中的調試,我把移植過程分為0~6共7個階段。每個階段完成時,u-boot都是可以正常運行的,因此,你可以根據自已的要求,決定移植工作做到哪一階段。為了同時支持S3C2440和S3C2410,我在移植時,同時加入兩種代碼,使用config_s3c2440,config_s3c2410這樣的巨集定義來決定編譯哪種代碼。如果你不需要同時支援兩個CPU,你可以只加入一種代碼。所有代碼不在此列出,大家可以看我上傳的移植好的u-boot。我在下面給出針對S3C2440移植的詳細說明,S3C2410的移植比較簡單,參考2440的移植即可,不另說明了。不同階段的詳細代碼可以閱讀patch檔。
第0階段:本階段任務,是在u-boot系統中,建立起自已的開發板體系。我先建立mini2440板的體系。方法如下。hugerat是我的網名,你可以根據需要更改。1 打開u-boot主目錄下的makefile,找到smdk2410_config,在其下,仿照它的格式加入如下語句rat2440_config : unconfig @$(MKCONFIG) $(@:_config=) arm arm920t rat2440 hugerat s3c24x0
各項的意思如下:arm: CPU的架構(ARCH)arm920t: CPU的類型(CPU),其對應於cpu/arm920t子目錄。rat2440: 開發板的型號(BOARD),對應於board/hugerat/rat2440目錄。hugerat: 開發者/或經銷商(vender)。s3c24x0: 片上系統(SOC)。
此步是為了加入自已的開發板,非必須也可以在現有的開發板基礎上修改。
2修改CROSS_COMPILE為自已的arm gcc編譯器,我使用的是系統默認的arm-linux-gcc,故不必再修改相應的CROSS_COMPILE項。
3 在/board子目錄中建立自己的開發板rat2440目錄由於我在上一步板子的開發者/或經銷商(vender)中填了 hugerat ,所以開發板rat2440目錄一定要建在/board子目錄中的hugerat
目錄下 ,否則編譯會出錯。然後,將smdk2410目錄下的檔考入此目錄中。並將其中的smdk2410.c改名為rat2440.c 還要記得修改自己的開發板rat2440目錄下的Makefile檔,不然編譯時會出錯:
COBJS := rat2440.o flash.o
4 在include/configs/中建立配置頭檔將smdk2410的相應頭檔複製一份在相同目錄下。並改名為rat2440.h
5 回到u-boot主目錄,make rat2440_config,再make,編譯生成u-boot.bin成功。
建立S3C410板的方法同上,僅將2440改為2410即可。不重複了。
第0階段完成。
第1階段本版u-boot依然沒有提供對S3C2440的支援,因此本階段任務是加入S3C2440相關的代碼,使得u-boot可以在s3c2440上正常工作。但沒有增加任何附加功能。在下列文件中加入標注為/*by hugerat,phase 1---*/的代碼。其中,被注釋掉的代碼為原代碼。/cpu/arm920t/start.s (加入S3C2440的時鐘相關的寄存器定義,加入時鐘初始化代碼,以使S3C2440工作在405MHz)board/hugerat/rat2440/lowlevel_init.s(加入S3C2440記憶體控制寄存器的定義)board/hugerat/rat2440/rat2440.c (修改GPIO,PLL的設置,應注意GPBCON的設置,不要讓蜂鳴器響)include/configs/rat2440.h(設定環境變數)以下檔主要加入CONFIG_S3C2440巨集定義以使得編譯一些S3C2410的代碼,和加入led燈的控制。
以指示u-boot程式進程。/inlcude/s3c24x0.hcpu/arm920t/s3c24x0/interrupts.c(這裏還要加入CONFIG_rat2440和CONFIG_rat2410)/cpu/arm920t/s3c24x0/serial.ccpu/arm920t/s3c24x0/speed.ccpu/arm920t/s3c24x0/usb_ohci.c/cpu/arm920t/s3c24x0/usb.c cpu/arm920t/s3c24x0/i2c.cdrivers/usb/usb_ohci.cdrivers/rtc/s3c24x0_rtc.c/lib_arm/board.c
編譯成功。將u-boot.bin燒入nor-flash即可運行。但是為方便使用,同時,也是方便u-boot的調試。因此,我要將此u-boot代碼再做一修改,使其可以在記憶體中運行。這樣,可以用開發板自帶的vivi將其下載到記憶體中,再在記憶體中運行u-boot。要想它在記憶體中運行,方法很簡單,將u-boot/cpu/arm920t中的start.s中
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
此段代碼中的bl cpu_init_crit注釋掉,即不進行CPU的初始化工作(此工作,當前在板子上運行的vivi已完成,故不能再次進行)
,即改為
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
@bl cpu_init_crit
#endif
修改/board/hugerat/rat2440/config.mk中text_base 值為0x33000000
使用vivi命令load ram 0x33000000 0x17ea8 x(0x17ea8 是載入檔案的size單位byte , x表示xmodem u表示usb)將u-boot.bin裝入記憶體。再用go 0x33000000命令,即可。
至此,第1階段工作完成。
第2階段本階段任務,是給u-boot移植dm9000的網卡驅動。u-boot自帶網卡驅動,所以只要做些設置即可。在下列檔中加入標誌為/*by hugerat,phase 2----*/的代碼./include/configs/rat2440.h (加入dm9000定義,加入ping命令定義)此時,編譯通過,但ping時,報`ethaddr' not set錯誤。研究原代碼,發現#define CONFIG_ETHADDR 08:00:3e:26:0a:5b被注釋掉,恢復,並改變原來默認的IP位址。重新編譯後,可以ping通網路了。(也可以在u-boot啟動後,修改相關參數,但因為現階段還沒有支持nandflash,參數無法保存,故在此改變較為方便)又發現,ping是能ping通了,但報could not establish link(不能建立鏈結)的錯。不影響使用。參考網上的資料,發現這是因為在網卡驅動中,/home/lijin/u-boot-rat/drivers/net/dm9000.c,有一段程式試圖連接網卡的MII介面,而實際上MII介面並未使用,所以有十秒的等待時間,且報錯,將此段程式注釋掉即可。到此,本階段任務完成。
第3階段本階段任務,移植nand-flash驅動,注意此階段工作僅是讓u-boot可以操作讀寫nand flash。還不能讓它從nand flash啟動。
首先,要說明一下CFG_NAND_LEGACY的使用。在u-boot的/drivers/mtd/下有兩個目錄,分別是nand和nand_legacy。在nand目錄下的是nand的初始化函數和nand的操作讀寫函數,是使用linux的mtd構架的。此目錄下的檔,只有在定義了CFG_CMD_NAND宏和沒有定義CFG_NAND_LEGACY巨集的情況下才會被編譯。在nand_leagcy目錄下的檔也是是實現nand相關操作命令,如read,write等命令的功能,但不是使用linux的mtd構架。此目錄下的檔,只有在定義了CFG_CMD_NAND和定義了CFG_NAND_LEGACY巨集的情況下才會定義。此目錄下的檔u-boot組織已不推薦使用。事實上,此版中,S3C2410構架已不支持對nand_leagcy,因此,我在移植中,是用的不定義CFG_NAND_LEGACY的方式,即非nand_leagcy方式。
在/include/configs/rat2440.h中加標注為by hugerat,phase3的代碼段,這段代碼是讓u-boot啟用其自帶nand flash驅動,並設置相應的nand flash參數。此時,試編譯一下,通過。要說明的是,此版的u-boot已自帶board_nand_init(),此函數在/cpu/arm920t/s3c24x0/nand.c中實現。並且此版已不支援定義CFG_NAND_LEGACY,如定義此宏,則編譯是會報 #error "U-Boot legacy NAND support not available
for S3C2410"的錯誤。故此版已不能使用網上流傳非nand_leagcy方式的自加nand flash初始化函數的方法,只能用其自帶的初始化函數下載到記憶體中運行,報None nand devices!!!,這是當然的。因為S3C2410和S3C2440在FLASH控制器上,差別較大,必須改寫代碼。
改寫主要在/cpu/arm920t/s3c24x0/nand.c中進行。(此檔內是與晶片緊密相關的代碼),一是board_nand_init函數,一是s3c2410_hwcontrol,參照2440的手冊,改寫相關代碼。還要在此檔的開始部分加入S3C2440 nand flash控制器相關寄存器的定義。改寫的代碼標注同上。改寫完畢後,u-boot可以識別出nand flash晶片是64MB的,但還不能識別是什麼晶片。
在/driver/mtd/nand/nand_base.c中的nand_get_flash_type函數結尾,修改MTDDEBUG語句,改為printf,再編譯,可以正常顯示芯片了。但nand write功能不正常,即沒報錯,實際上也沒有寫進去。
探討原因後發現,原來是u-boot自帶的nand-flash驅動(不定義nand_leagcy),是基於mtd驅動的。在默認情況下,不進行寫入正確與否的校驗。要定義CONFIG_MTD_NAND_VERIFY_WRITE宏才能進行寫入校驗。關於ECC校驗,mtd驅動默認是用sotf_ecc的。加入宏定義後,u-boot報write error了。在此我卡了很久,最後才發現是u-boot.2008.10自帶的S3C2410的s3c2410_hwcontrol函數有錯。在此函數中,把chip->IO_ADDR_W值改寫了,導致在寫資料時出現錯誤。將此錯誤修正後,nand write正常了。修正方法是使用一總體變數替代chip->IO_ADDR_W。
接下來,在rat2440.h中加入#define CONFIG_ENV_IS_IN_NAND 1注掉原來的#define CONFIG_ENV_IS_IN_FLASH 1,加入
#define CONFIG_ENV_OFFSET 0x30000 注掉原來的#define CFG_ENV_OFFSET 0x30000。編譯。saveenv功能也正常了。至此,nand-flash驅動移植完成。
測試,nand write 0x30000000 0x40000 0x40000時,成功。用nand read也成功讀出。此處要說明的,如果用vivi燒寫資訊到nand
中,再用u-boot讀取,會報錯,應該是ECC校驗不是由同一軟體產生所至。此階段完成。
第4階段本階段任務是將u-boot改寫為從nand-flash啟動。首先,將/cpu/arm920t/start.s中第一階段中為了從記憶體中啟動而遮罩掉的語句恢復。加入標注為/*by hugerat,phase 4----*/的
語句,為了相容S3C2410,同時也加入S3C2410 nand boot 相關代碼。
同時,在/board/hugerat/rat2440/lower_init.s中,根據開發板電路,對記憶體相關的幾個寄存器定義進行調整。整個啟動代碼參考了vivi的代碼。因此,將vivi的nand_read.c(支持S3C2440
的vivi,此檔中的讀nand函數是直接操作nand flash的,與u-boot自帶,適合用於啟動時用。)考入/board/hugerat/rat2440目錄下。並修改目錄下的makefile,使nand_read.c被編譯。編譯,成功,將u-boot燒入nand flash,能成功從nand啟動了。
第5階段本階段任務是實現u-boot通過nfs引導友善提供的linux2.6.13的內核。友善的linux並沒有提供make uImage命令,需要添加。將u-boot的tools目錄下的mkimage拷到開發機linux系統下的/usr/bin目錄下
。在linux的/arch/arm/makefile中和/arch/arm/boot/makefile中,可以找到zImage的項目,仿照它添加uImage項目。
這個問題解決後,就是要用u-boot引導linux內核了,我用的是揚創提供的linux2.6.13的內核,揚創提供的內核,loadaddress和entry都是30008000,我用u-boot1.2中的bootcmd參數,u-boot1.3.1 bootm卻不能引導,報bad magic number,看了bootm的相關資料,得知,如果我們沒用mkimage對內核進行處理的話,那直接把內核下載到0x30008000再運行就行,內核會自解壓運行(不過內核運行需要一個tag來傳遞參數,而這個tag建議是由bootloader提供的,在u-boot下默認是由bootm命令建立的)。 2)如果使用mkimage生成內核鏡像檔的話,會在內核的前頭加上了64byte的資訊,供建立tag之用。bootm命令會首先判斷bootm xxxx 這個指定的位址xxxx是否與-a指定的載入位址相同。(1)如果不同的話會從這個位址開始提取出這個64byte的頭部,對其進行分析,然後把去掉頭部的內核複製到-a指定的load地址中去運行之(2)如果相同的話那就讓其原封不同的放在那,但-e指定的入口地址會推後64byte,以跳過這64byte的頭部。
Bootm在沒有參數時,是採用rat2440.h中的#define CFG_LOAD_ADDR 的位址的,而我用bootm就是沒有使用參數,所以出錯了。正確的做法應該是用nand read命令將內核從nand flash中讀到記憶體的某一位址中(注意不要與其他已分配的記憶體衝突),然後再用bootm 加位址參數,即可引導,也可以在上述的檔中,將CFG_LOAD_ADDR的位址定義為此位址,再用bootm就可以了.我設定bootcmd環境變數為tftp 0x31000000 uImage; bootm 0x31000000,注意位址不能為0x30008000,否則報錯.
做完這此,內核可以引導了,但卻停在starting kernel不動了,好在我以前做過vivi+linux2.6.22的移植,知道此問題多半是由於mach_type不同而造成的。在u-boot中,此mach_type是由rat2440.c中的這段代碼定義的
#if defined(CONFIG_S3C2440)
/* arch number of S3C2440 -Board */
//gd->bd->bi_arch_number = 5244 ; //改為和內核的MACH_TYPE一至
gd->bd->bi_arch_number = MACH_TYPE_MINI2440 ;
#endif
你可以直接在這裏改數位,也可以在include/asm-arm/mach_types.h的文件中,改MACH_TYPE_S3C2440的數值。將數值改為和內核的
mach_type一至。至於內核的mach_type可以在內核linux源代碼下的arch/arm/tools中的mach_types檔查看到。
此時,又發現內核不能通過nfs引導。查資料後得知是因為u-boot沒有傳遞參數給內核。原來是rat2440.h中少定義了幾個宏。補上
。#define CONFIG_CMDLINE_TAG 1 /* enable passing of ATAGs */
#define CONFIG_SETUP_MEMORY_TAGS 1
#define CONFIG_INITRD_TAG 1
至此,u-boot已能正常引導linux啟動了。第5階段完成。
第6階段本階段任務,是給u-boot加入燒寫yaffs映射檔的功能。網上有很多文章已經有介紹如何加入此功能,方法也不複雜。但我移植的u-boot.2008.10版,在nand flash的操作上與之前的版本
有了很大的區別。這些文章介紹的方法已不能在此版中應用。於是我只好自已根據u-boot的源代碼,yaffs映射檔的格式,自已編修改了u-boot.2008.10的代碼,讓它可以實現yaffs的燒寫,因為我用的開發板的nand flash是k9f1208,只能使用yaffs1,所以我的修改代碼暫時只能支援yaffs1格式的映射。(要支持yaffs2也不難,但因為無法驗證,所以就沒有做嘗試。)修改方法如下:在/common/com_nand.c中do_nand函數中,加入三段/*by hugerat,phase6---*/標注的代碼,實現對nand write.yaffs命令的支援。
此代碼中,要用到mtd_info結構中的兩個變數,這兩個變數本來是沒有的,所以要在include/linux/mtd/mtd.h的mtd_info結構體定
義中加入。在/drivers/mtd/nand/nand_util.c的nand_write_skip_bad函數中,兩段程式,一段是為了計算正常資料的長度,一段是為了在寫入一段資料後,資料指標能正常跳到下一段資料。在/drivers/mtd/nand/nand_base.c的nand_write函數中,加入一段把正常資料與oob資料分離的代碼,再加入頁寫時的模式設置為MTD_OOB_RAW,使頁寫時,不進行ECC的校驗和加入。(ECC的校驗在yaffs的oob資料中已自帶了,不能重寫。)此模式下,寫入正常資料後,會把oob資料緩存的資料寫入nand的oob區。至此,u-boot.2008.10的移植工作全部完成。
最後說明一下,關於陽初2410的移植,因為上面的代碼中已加入S3C2410相關代碼,移植時,只需參考S3C2440,新建一個開發板體系,新建時,將rat2440目錄下的檔拷貝到rat2410下,將重命名S3C2440.c為S3C2410.c,修改makefile,加上s3c2410.o 去掉s3c2440.o,拷貝rat2440.h為rat2410.h,將其中的CONFIG_S3C2440注掉,加上CONFIG_S3C2410。在u-boot總目錄下的makefile中rat2440_config下添加rat2410_config : unconfig @$(MKCONFIG) $(@:_config=) arm arm920t rat2410 hugerat s3c24x0因為陽初的板用的是cs8900,因此要在rat2410.h中注掉DM9000的定義,添加CS8900的。再make rat2410_config,再make,即可。
文章最後,為方便網友,我把patch檔附上。patch文件使用方法是在linux下建立u-boot-rat.patch檔,將patch檔的內容拷入u-boot-rat.patch文件中。下載u-boot-2008.10.tar.bz2,在這裏下載ftp://ftp.denx.de/pub/u-boot/,用tar -xvf u-bot-2008.19.tar.bz2解壓到u-boot-2008.10目錄,把u-boot-rat.patch檔拷貝到此目錄所在同級目錄下。執行patch -p0 小於符號 u-boot-rat.patch。完成後,進u-boot-2008.10目錄,執行make rat2440_config,再make即可生成u-boot.bin,如是2410,則執行make rat2410_config即可,我在mini2440,陽初的2410上試過,功能正常。其他開發板的也可一試。 附patch文件: 可能發貼字數有限制,下面的補丁檔不全。可以在此下載全部補丁。 文件:u-boot-rat.rar 大小:20KB 下載 。
--> 閱讀更多...