本文全过程为自己亲自试验,其中经历了很多挫折,但功夫不负有心人,终于在自己的努力下(当然也要感谢写博客的朋友们)成功移植了一个最基本功能的Kernel、根文件系统,现在将这个一耗时、耗精力 的过程写下来,希望对遇到相同问题的朋友们有所帮助!
平台:飞凌FL2440
windows xp sp2
vmware 7.0.0 build-203739
ubuntu 12.04
交叉编译器: 尝试了4.3.2 、4.5.1、3.4.1 (busybox编译的根文件系统最后在3.4.1中通过,为什么用了这么多,后面会解释)
内核:linux-2.6.28.7.tar.bz2
根文件系统:busybox-1.19.4 busybox-1.19.4.tar.bz2
一、Kernel的移植:
整个过程参考的是这篇文章http://wenku.baidu.com/view/6cf79631eefdc8d376ee32d2.html,但该文章有些问题,下面的步骤对其进行了更改。
移植步骤:
1,我们首先在根目录下新建FL2440目录,
mkdir /FL2440
然后到Linux 内核官网下载Linux-2.6.28.7 的内核源码包。地址如下
http://www.kernel.org/pub/linux/kernel/v2.6/
或者
ftp://ftp.kernel.org/pub/linux/kernel/v2.6/
下载完毕,将内核源码包放到/FL2440目录下,然后解压缩
tar –jxvf linux-2.6.28.7.tar.bz2
FL2440目录下会多一个linux-2.6.28.7 的目录
2,由于该内核源码包中,没有yaffs2 驱动,所以我们要将yaffs2 文件系统的驱动添加到内核
源码中。yaffs2的补丁包需要登陆http://www.yaffs.net/download-yaffs-using-git,通过GIt方式获取(在Ubuntu中git clone git://www.aleph1.co.uk/yaffs2)或者到Linuxidc网站下载压缩包,由于时间有点长,记不住自己用的那种方式了,下载完后解压,执行./patch-ker.sh c m
/FL2440/linux-2.6.28.7 为kernel打上补丁,显示:
Updating /FL2440/linux-2.6.28.7/fs/Kconfig
Updating /FL2440/linux-2.6.28.7/fs/Makefile
如果上面的过程还需要其他的库或工具,需要通过sudo apt-get install ***来安装相应的工具
3. 修改linux-2.6.28.7/Makefile,将
ARCH ?=$ (SUBARCH)
CROSS_COMPILE ?=
修改为
ARCH ?= arm
CROSS_COMPILE ?= arm-linux-
注意,这里是在交叉编译环境已经建立完成的情况下的,并且,arm-linux-已经添加进入了环
境变量中。如果没做完这步,那么你要先建立交叉编译环境,并把arm-linux-导入到环境变量
中。这里也可以直接添加,如CROSS_COMPILE ?=/FL2440/usr/local/arm/4.3.2/bin/arm-none-linux-gnueabi-
我采用的后一种,如果用前面一种,需要更改/etc/bash.bashrc文件
#vim /etc/bash.bashrc
在最后加上:
export PATH=$PATH:/usr/local/arm/3.4.1/bin
export PATH
另外还有其他几种方法,百度一下吧, 安装成功的标志arm-linux-gcc -v 以及 echo $PATH会返回相应信息。好像需要注销生效,或者source /root/.bashrc不用重启生效
4,修改机器码。飞凌开发板的bootloader 默认的机器码是193,所以我们在使用FL2440 机器
的时候,需要修改机器码。修改arch/arm/tools/mach-types。将
s3c2440 ARCH_S3C2440 S3C2440 362
修改为193。
这里如果在配置时使用
[*] SMDK2410/A9M2410
则这里就不用修改了,当然了,我们一般不选SMDK2410/A9M2410 ,所以要修改。
5,由于板子使用的是12MHz 的晶振,而我们使用的SMDK2440 的晶振频率不是这个值,所
以我们要修改,要不然,会出现打印信息乱码的现象。
文件:arch/arm/mach-s3c2440/mach-smdk2440.c
函数:static void __init smdk2440_map_io(void)
/*s3c24xx_init_clocks(16934400);*/ s3c24xx_init_clocks(12000000);
6,修改MTD 分区。由于fl2440 和smdk2440 的nandflash 分区不同,所以这里要修改,要不
然会在启动的过程中会出现错误。需要和bootloader 中的分区信息相同(128M),否则,启
动时出错。
文件: arch/arm/plat-s3c24xx/common-smdk.c
结构体:static struct mtd_partition smdk_default_nand_part[]
将源结构体删除,或者注释掉,修改为以下形式
static struct mtd_partition smdk_default_nand_part[] = {
[0] = {
.name = "Boot",
.size = 0x00100000,
.offset = 0
},
[1] = {
.name = "MyApp",
.size = 0x003c0000,
.offset = 0x00140000,
},
[2] = {
.name = "Kernel",
.size = 0x00300000,
.offset = 0x00500000,
},
[3] = {
.name = "fs_yaffs",
.size = 0x03c00000, //30M
.offset = 0x00800000,
},
[4] = {
.name = "WINCE",
.size = 0x03c00000,
.offset = 0x04400000,
}
};
这里注意,一定要将fs_yaffs 对应到3 上面,并且内容要和板子上面的bootloader 一致,要不
然会出现kernel panic 的错误。其他可以随意。
7,关闭ECC 校验。
文件:drivers/mtd/nand/s3c2410.c
函数:s3c2410_nand_init_chip
/*chip->ecc.mode = NAND_ECC_SOFT; */ chip->ecc.mode = NAND_ECC_NONE;
8,修改nandflash 驱动,支持fl2440 开发板的nandflash
文件:drivers/mtd/nand/nand_bbt.c
static struct nand_bbt_descr largepage_memorybased = {
.options = 0,
.offs = 0,
.len = 1, // 原数值为2,支持2K 每页的flash 修改为1。K9F1G08,K9F2G08
是2k 每页的flash
.pattern = scan_ff_pattern
};
static struct nand_bbt_descr largepage_flashbased = {
.options = NAND_BBT_SCAN2NDPAGE,
.offs = 0,
.len = 1, //原数值为2,支持2K 每页的flash 修改为1。K9F1G08,K9F2G08
是2k 每页的flash
.pattern = scan_ff_pattern
};
9,增加devfs 文件管理器的支持。我们所用的文件系统使用的是devfs 文件管理器。
文件:fs/Kconfig,
添加
config DEVFS_FS
bool "/dev file system support (OBSOLETE)"
default y
config DEVFS_MOUNT
bool "Automatically mount at boot"
default y
depends on DEVFS_FS
10, 如果不考虑网络设备、LCD 显示屏、触摸屏等外围设备,这时就可以进行配置内核了。
我们把s3c2410 的默认配置写入config 文件。命令如下
make s3c2410_defconfig
输入如下命令进行内核配置,配置的过程,只将提到的修改,其他默认。
make menuconfig
配置文件系统选项,配置yaffs2 文件系统 ,修改配置如下
File systems --->
[*] Miscellaneous filesystems --->
<*> yaffs2 file system support
-*- 512 byte / page devices
[ ] Use older-style on-NAND data format with pageStatus byte
[ ] Lets yaffs do its own ECC (NEW)
-*- 2048 byte (or larger) / page devices
[*] Autoselect yaffs2 format (NEW)
[ ] Disable yaffs from doing ECC on tags by default (NEW)
配置cpu相关选项,修改配置如下:
System Type --->
S3C2440 Machines --->
[*] SMDK2440
[*] SMDK2440 with S3C2440 CPU module
去掉S3C2400 Machines、S3C2410 Machines、S3C2412 Machines、S3C2442 Machines的所有
选项。如果现在编译内核,下载到开发板中,内核就可以正常启动了。
编译内核使用命令如下:
make zImage
生成的内核文件在arch/arm/boot下面,文件为zImage。现在烧写到板子上面,就能够启动
内核了,可是这还不行,还会出现lcd显示不正常,触屏不能使用等情况,这是由于没有移植
相应的驱动或者驱动程序的参数不正确的原因。下面就对其进行改正。
11, 移植USB host 驱动。
在这个版本的linux 内核,已经对USB 驱动进行来很好的支持,仅仅需要修改配置。
Device Drivers --->
[*] USB support --->
{*} Support for Host-side USB
[*] USB device filesystem (DEPRECATED)
[*] USB device class-devices (DEPRECATED)
<*> OHCI HCD support
<*> USB Mass Storage support
[*] HID Devices --->
{*} Generic HID support
[*] /dev/hidraw raw HID device support
SCSI device support --->
<*> SCSI device support
[*] legacy /proc/scsi/ support
<*> SCSI disk support
<*> SCSI tape support
如果从来没有配置过内核的话,基本上上面的都是默认的。不过 <M> USB Mass Storage
support 改为<*>,而默认[ ] /dev/hidraw raw HID device support 要改为<*>,而<M>
SCSI tape support 改为<*>。
目前2.6.28.7 版本的根文件系统的设备管理器是静态生成节点的,所以需要添加节点。开发板
启动以后,在/dev 目录下输入一下命令:
mknod sda1 b 8 1
这个命令中的sda1 是设备节点名
12, 移植RTC 驱动
在这个版本的linux 内核,已经对RTC 驱动进行来很好的支持,不需要修改配置。相应配置如
下
Device Drivers --->
<*> Real Time Clock --->
[*] Set system time from RTC on startup and resume
(rtc0) RTC used to set the system time
[ ] RTC debug support
*** RTC interfaces ***
[*] /sys/class/rtc/rtcN (sysfs)
[*] /proc/driver/rtc (procfs for rtc0)
[*] /dev/rtcN (character devices)
<*> Samsung S3C series SoC RTC
以上都是默认的。
然后添加对设备的支持
打开arch/arm/mach-s3c2440/mach-smdk2440.c,添加设备,代码如下:
static struct platform_device *smdk2440_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_iis,
&s3c_device_rtc,
};
13, 移植UDA1341 驱动
由于这里要用到几个头文件,而Linux2.6.28.7 内核源码中没有,所以要创建。
首先是在include/sound 下建立s3c24xx_uda134x.h,内容为
#ifndef _S3C24XX_UDA134X_H_
#define _S3C24XX_UDA134X_H_ 1
#include <sound/uda134x.h>
struct s3c24xx_uda134x_platform_data {
int l3_clk;
int l3_mode;
int l3_data;
void (*power) (int);
int model;
};
#endif
然后在include/sound/下建立uda134x.h 文件,内容为:
#ifndef _UDA134X_H
#define _UDA134X_H
#include <sound/l3.h>
struct uda134x_platform_data {
struct l3_pins l3;
void (*power) (int);
int model; } //原文有错误,括号打错了
#define UDA134X_UDA1340 1
#define UDA134X_UDA1341 2
#define UDA134X_UDA1344 3
#endif /* _UDA134X_H */
接着在include/sound 下建立l3.h 文件,内容为
#ifndef _L3_H_
#define _L3_H_ 1
struct l3_pins {
void (*setdat)(int);
void (*setclk)(int);
void (*setmode)(int);
int data_hold;
int data_setup;
int clock_high;
int mode_hold;
int mode;
int mode_setup;
};
int l3_write(struct l3_pins *adap, u8 addr, u8 *data, int len);
#endif
在arch/arm/mach-s3c2410/include/mach/下建立gpio-fns.h,内容为
/* s3c2410_gpio_cfgpin
*
* set the configuration of the given pin to the value passed.
*
* eg:
* s3c2410_gpio_cfgpin(S3C2410_GPA(0), S3C2410_GPA0_ADDR0);
* s3c2410_gpio_cfgpin(S3C2410_GPE(8), S3C2410_GPE8_SDDAT1);
*/
extern void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function);
extern unsigned int s3c2410_gpio_getcfg(unsigned int pin);
/* s3c2410_gpio_getirq
*
* turn the given pin number into the corresponding IRQ number
*
* returns:
* < 0 = no interrupt for this pin
* >=0 = interrupt number for the pin
*/
extern int s3c2410_gpio_getirq(unsigned int pin);
#ifdef CONFIG_CPU_S3C2400
extern int s3c2400_gpio_getirq(unsigned int pin);
#endif /* CONFIG_CPU_S3C2400 */
/* s3c2410_gpio_irqfilter
*
* set the irq filtering on the given pin
*
* on = 0 => disable filtering
* 1 => enable filtering
*
* config = S3C2410_EINTFLT_PCLK or S3C2410_EINTFLT_EXTCLK orred with
* width of filter (0 through 63)
*
*
*/
extern int s3c2410_gpio_irqfilter(unsigned int pin, unsigned int on,
unsigned int config);
/* s3c2410_gpio_pullup
*
* configure the pull-up control on the given pin
*
* to = 1 => disable the pull-up
* 0 => enable the pull-up
*
* eg;
*
* s3c2410_gpio_pullup(S3C2410_GPB(0), 0);
* s3c2410_gpio_pullup(S3C2410_GPE(8), 0);
*/
extern void s3c2410_gpio_pullup(unsigned int pin, unsigned int to);
/* s3c2410_gpio_getpull
*
* Read the state of the pull-up on a given pin
*
* return:
* < 0 => error code
* 0 => enabled
* 1 => disabled
*/
extern int s3c2410_gpio_getpull(unsigned int pin);
extern void s3c2410_gpio_setpin(unsigned int pin, unsigned int to);
extern unsigned int s3c2410_gpio_getpin(unsigned int pin);
在平台上添加和配置UDA1341:
修改arch/arm/mach-s3c2440/mach-smdk2440.c。在开始添加头文件
#include <sound/s3c24xx_uda134x.h>
#include <mach/gpio-fns.h>
添加设备配置
static struct s3c24xx_uda134x_platform_data s3c24xx_uda134x_data = {
.l3_clk = S3C2410_GPB4,
.l3_data = S3C2410_GPB3, //原文有错误
.l3_mode = S3C2410_GPB2,
.model = UDA134X_UDA1341,
};
static struct platform_device s3c24xx_uda134x = {
.name = "s3c24xx_uda134x",
.dev = {
.platform_data = &s3c24xx_uda134x_data,
}
};
把设备添加到平台当中
static struct platform_device *smdk2440_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_iis,
&s3c_device_rtc,
&s3c24xx_uda134x,
};
配置如下
Device Drivers --->
<*> Sound card support --->
<*> Advanced Linux Sound Architecture --->
<*> OSS Mixer API
<*> OSS PCM (digital audio) API
[*] OSS PCM (digital audio) API - Include plugin system
[*] Support old ALSA API
[*] Verbose procfs contents
[*] Verbose printk
[*] Generic sound devices --->
<*> ALSA for SoC audio support --->
<*> SoC Audio for the Samsung S3C24XX chips
<*> SoC I2S Audio support UDA134X wired to a S3C24XX
配置前还有改变sound/soc/s3c24xx/Kconfig 文件
在里面添加如下内容
config SND_S3C24XX_SOC_S3C24XX_UDA134X
tristate "SoC I2S Audio support UDA134X wired to a S3C24XX"
depends on SND_S3C24XX_SOC && ARCH_S3C2410
select SND_S3C24XX_SOC_I2S
select SND_SOC_L3
select SND_SOC_UDA134X
只有这样,最后蓝色部分才会出现
14, 移植DM9000 驱动
修改 drivers/net/dm9000.c 文件,头文件增加
#include <mach/regs-gpio.h>
#include <mach/irqs.h>
#include <mach/hardware.h>
在dm9000_probe 函数开始增加:
unsigned char ne_def_eth_mac_addr[]={0x00,0x12,0x34,0x56,0x80,0x49};
static void *bwscon;
static void *gpfcon;
static void *extint0;
static void *intmsk;
#define BWSCON (0x48000000)
#define GPFCON (0x56000050)
#define EXTINT0 (0x56000088)
#define INTMSK (0x4A000008)
bwscon=ioremap_nocache(BWSCON,0x0000004);
gpfcon=ioremap_nocache(GPFCON,0x0000004);
extint0=ioremap_nocache(EXTINT0,0x0000004);
intmsk=ioremap_nocache(INTMSK,0x0000004);
writel(readl(bwscon)|0xc0000,bwscon);
writel( (readl(gpfcon) & ~(0x3 << 14)) | (0x2 << 14), gpfcon);
writel( readl(gpfcon) | (0x1 << 7), gpfcon); // Disable pull-up
writel( (readl(extint0) & ~(0xf << 28)) | (0x4 << 28), extint0); //rising edge
writel( (readl(intmsk)) & ~0x80, intmsk);
在这个函数的最后需要修改:
if (!is_valid_ether_addr(ndev->dev_addr)) {
/* try reading from mac */
mac_src = "chip";
for (i = 0; i < 6; i++)
//ndev->dev_addr[i] = ior(db, i+DM9000_PAR);
ndev->dev_addr[i] = ne_def_eth_mac_addr[i];
}
修改arch/arm/mach-s3c2440/mach-smdk2440.c,添加设备
static struct platform_device *smdk2440_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_iis,
&s3c_device_rtc,
&s3c24xx_uda134x,
&s3c_device_dm9000,
修改 arch/arm/plat-s3c24xx/devs.c,添加头文件
#include <linux/dm9000.h>
static struct resource s3c_dm9000_resource[] = {
[0] = {
.start = S3C24XX_PA_DM9000,
.end = S3C24XX_PA_DM9000+ 0x3,
.flags = IORESOURCE_MEM
},
[1]={
.start = S3C24XX_PA_DM9000 + 0x4, //CMD pin is A2
.end = S3C24XX_PA_DM9000 + 0x4 + 0x7c,
.flags = IORESOURCE_MEM
},
[2] = {
.start = IRQ_EINT7,
.end = IRQ_EINT7,
.flags = IORESOURCE_IRQ
},
};
static struct dm9000_plat_data s3c_device_dm9000_platdata = {
.flags= DM9000_PLATF_16BITONLY,
};
struct platform_device s3c_device_dm9000 = {
.name= "dm9000",
.id= 0,
.num_resources= ARRAY_SIZE(s3c_dm9000_resource),
.resource= s3c_dm9000_resource,
.dev= {
.platform_data = &s3c_device_dm9000_platdata,
}
};
EXPORT_SYMBOL(s3c_device_dm9000);
最后蓝色的部分,似乎其他的版本没有
修改 arch/arm/plat-s3c24xx/include/plat/devs.h
或者有的版本是arch/arm/plat-s3c/include/plat/devs.h
40 行附近,添加
extern struct platform_device s3c_device_dm9000;
修改arch/arm/mach-s3c2410/include/mach/map.h 文件118 行左右
/* DM9000 */
#define S3C24XX_PA_DM9000 0x20000300
#define S3C24XX_VA_DM9000 0xE0000000
或者是
/* DM9000 */
#define S3C24XX_PA_DM9000 0x20000300
#define S3C24XX_VA_DM9000 S3C2410_ADDR(0x02100300)
15, 可能你会看到有的板子上面能够在开机的过程中显示一个企鹅或者其他的图像,如果
要是显示企鹅的话,很简单,只需在配置内核过程中将下面进行配置就可以了。
Graphics support --->
<*> Support for frame buffer devices---à
<*> S3C2410 LCD framebuffer support ,multi support!
Console display driver support --->
<*> Framebuffer Console support
[*] Bootup logo ---à
[*] Standard 224-color Linux logo
这样配置后,编译的内核文件就能显示一个企鹅
16, 3.5 寸LCD 显示的移植 (注意,这里要根据LCD此村进行更改)
更改arch/arm/mach-s3c2440/mach-smdk2440.c
/* LCD driver info */
static struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {
.lcdcon5 = S3C2410_LCDCON5_FRM565 |
S3C2410_LCDCON5_INVVLINE |
S3C2410_LCDCON5_INVVFRAME |
S3C2410_LCDCON5_PWREN |
S3C2410_LCDCON5_HWSWP,
.type = S3C2410_LCDCON1_TFT,
.width = 320,
.height = 240,
.pixclock = 156250,
.xres = 320,
.yres = 240,
.bpp = 16,
.left_margin = 20,//or8,
.right_margin = 38,//or5,
.hsync_len = 30,//or63,
.upper_margin = 12,//or15,
.lower_margin = 15,//or3,
.vsync_len =3,//or5,
};
static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {
.displays = &smdk2440_lcd_cfg,
.num_displays = 1,//或者ARRAY_SIZE(smdk2440_lcd_cfg),
.default_display = 0,
#if 0
/* currently setup by downloader */
.gpccon = 0xaa940659,
.gpccon_mask = 0xffffffff,
.gpcup = 0x0000ffff,
.gpcup_mask = 0xffffffff,
.gpdcon = 0xaa84aaa0,
.gpdcon_mask = 0xffffffff,
.gpdup = 0x0000faff,
.gpdup_mask = 0xffffffff,
#endif
.lpcsel =0,//((0xCE6) & ~7) | 1<<4,
};
上面红色部分必须修改,否则显示会不正常。其他参数默认。当然这里不是随便改的,具体如
何改,稍后详解。注意红色参数后面的注释
LCD 的参数设定是需要根据LCD 的手册来设定arch/arm/mach-s3c2440/mach-smdk2440.c 里面
的s3c2410fb_display smdk2440_lcd_cfg 结构体
例如从LQ035NC111 的手册可以得到如下一个表
该表描述了该款并行LCD 的所有时钟需求,在这里我参照的全是典型值“Typ”栏
一个很具有参考价值的文档文件是 Documentation/fb/framebuffer.txt 文件,里面给我们描述了
一个架构
还有一个很有用的公式 Pixelclock:
xfree: in MHz
fb: in picoseconds (ps)
pixclock = 1000000 / DCF
再结合结构体 static struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {
.lcdcon5 = S3C2410_LCDCON5_FRM565 |
S3C2410_LCDCON5_INVVLINE |
S3C2410_LCDCON5_INVVFRAME |
S3C2410_LCDCON5_PWREN |
S3C2410_LCDCON5_HWSWP,
.type = S3C2410_LCDCON1_TFT,
.width = LCD_WIDTH,
.height = LCD_HEIGHT,
.pixclock = LCD_PIXCLOCK,
.xres = LCD_WIDTH,
.yres = LCD_HEIGHT,
.bpp = 16,
.left_margin = LCD_LEFT_MARGIN,
.right_margin = LCD_RIGHT_MARGIN,
.hsync_len = LCD_HSYNC_LEN,
.upper_margin = LCD_UPPER_MARGIN ,
.lower_margin = LCD_LOWER_MARGIN,
.vsync_len = LCD_VSYNC_LEN,
};
pixclock:现在我们就可以开始设置这个结构体的参数了,有上面的表我们可以知道LCD 的
时钟Dclk 应该是156ns,这个对应结构体里面的像素点时钟pixclock,在来看看第四节提到的
一个公式 pixclock=1000000/DCF,这个DCF 就是LCD 的Dclk 对应的频率,注意,单位为MHz,
所以 DCF=1000 000 000/156 Hz=1000/156 MHz;可以得到 pixclock=1000000/(1000/156)
=156000;
width、height 的设定这个就没什么歧义了,对应320 和240
bpp:其实我的这个LCD 手册上说该屏是支持24 位色的,但是这里填写16 位,有空可以试试
24 位
其他的参数:其他参数对应第3 节的表填写
xres <===========> TEP(Thd)(Hsync Display Period) LCD_WIDTH
yres <===========> Tvd(Vsync Display Period)LCD_HEIGHT,
left_margin <===========> Thf(Hsync Front-Porch)LCD_LEFT_MARGIN,
right_margin <===========> Thb(Hsync Back-Porch)LCD_RIGHT_MARGIN
hsync_len <===========> THS(Thp)(Hsync Pulse Width)
upper_margin <===========> Tvf(Vsync Front-Porch)
lower_margin <===========> Tvb(Vsync Back-Porch )
vsync_len <===========> Tvs(Tvp)(Vsync Pulse Width)
fl2440 用的lcd 手册中的表格如下
从而可知,
参数对应为
.width = 320,
.height = 240,
.pixclock = 156000,
.xres =320,
.yres = 240,
.bpp = 16,
.left_margin = 20,
.right_margin = 38,
.hsync_len = 30,
.upper_margin = 12,
.lower_margin = 15,
.vsync_len = 3,
17, 移植看门狗
修改配置
Device Drivers --->
[*] Watchdog Timer Support --->
<*> S3C2410 Watchdog
即可。其实默认配置就是这样的。但是要是打开看门狗还需要修改源码。
18, 触摸屏驱动移植
文件:arch/arm/plat-s3c24xx/devs.c
头文件添加
#include<mach/s3c2410_ts.h> //y原文有误
在最后一行的上面添加如下代码
/* Touchscreen */
struct platform_device s3c_device_ts = {
.name = "s3c2410-ts",
.id = -1,
};
EXPORT_SYMBOL(s3c_device_ts);
static struct s3c2410_ts_mach_info s3c2410ts_info;
void set_s3c2410ts_info(struct s3c2410_ts_mach_info *hard_s3c2410ts_info)
{
memcpy(&s3c2410ts_info,hard_s3c2410ts_info,sizeof(struct
s3c2410_ts_mach_info));
s3c_device_ts.dev.platform_data = &s3c2410ts_info;
}
EXPORT_SYMBOL(set_s3c2410ts_info);
在arch/arm/mach-s3c2410/include/mach/下建立文件s3c2410_ts.h,内容为:
/* linux/include/asm/arch-s3c2410/s3c2410_ts.h
*
* Copyright (c) 2005 Arnaud Patard <arnaud.patard@rtp-net.org>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*
* Changelog:
* 24-Mar-2005 RTP Created file
*/
#ifndef __ASM_ARM_S3C2410_TS_H
#define __ASM_ARM_S3C2410_TS_H
struct s3c2410_ts_mach_info {
int delay;
int presc;
int oversampling_shift;
};
void __init set_s3c2410ts_info(struct s3c2410_ts_mach_info *hard_s3c2410ts_info);
#endif /* __ASM_ARM_S3C2410_TS_H */
修改arch/arm/mach-s3c2440/mach-smdk2440.c
添加头文件#include<mach/s3c2410_ts.h>
并在static struct platform_device *smdk2440_devices[] __initdata 结构体数组内添加
&s3c_device_adc,(触摸屏需要ADC 的支持)
&s3c_device_ts, //最好将adc 添加到ts 前面,否则触摸屏初始化时可能会出错//
并在上面结构体后面添加:
/*Config for TouchScreen*/
static struct s3c2410_ts_mach_info smdk2410_ts_cfg __initdata = {
.delay = 20000,
.presc = 49,
.oversampling_shift = 2,
};
最后在static void __init smdk2440_machine_init(void)函数中增加下列代码:
set_s3c2410ts_info(&smdk2410_ts_cfg); //原文有误
由于2.6.22.7 中没有触屏驱动,所以要添加上。而在2.6.33 上面就有了,所以这里改变的要大
些。
在drivers/input/touchscreen 下建立ts.h 和s3c2410_ts.c 文件,内容分别为:
ts.h 内容:
/* linux/include/asm/arch-s3c2410/ts.h
*
* Copyright (c) 2005 Arnaud Patard <arnaud.patard@rtp-net.org>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*
* Changelog:
* 24-Mar-2005 RTP Created file
* 03-Aug-2005 RTP Renamed to ts.h
*/
#ifndef __ASM_ARM_TS_H
#define __ASM_ARM_TS_H
struct EmbedSky_ts_mach_info {
int delay;
int presc;
int oversampling_shift;
};
void __init set_EmbedSky_ts_info(struct EmbedSky_ts_mach_info *hard_EmbedSky_ts_info);
#endif /* __ASM_ARM_TS_H */
s3c2410_ts.c 内容为:
/#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/s3c2410_ts.h>
#include <plat/regs-adc.h>
#define TRUE 1 //CoAsia added
#define FALSE 0 //CoAsia added
#define FILTER_LIMIT 25 //CoAsia added
/* For ts.dev.id.version */
#define S3C2410TSVERSION 0x0101
#define TSC_SLEEP (S3C2410_ADCTSC_PULL_UP_DISABLE |
S3C2410_ADCTSC_XY_PST(0))
#define WAIT4INT(x) (((x)<<8) | \
S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN |
S3C2410_ADCTSC_XP_SEN | \
S3C2410_ADCTSC_XY_PST(3))
#define AUTOPST (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN |
S3C2410_ADCTSC_XP_SEN | \
S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0))
#define DEBUG_LVL "<3>" //KERN_DEBUG
MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
MODULE_DESCRIPTION("s3c2410 touchscreen driver");
MODULE_LICENSE("GPL");
/*
* Definitions & global arrays.
*/
static char *s3c2410ts_name = "s3c2410 TouchScreen";
/*
* Per-touchscreen data.
*/
struct s3c2410ts {
struct input_dev *dev;
long xp;
long yp;
int count;
int shift;
};
static struct s3c2410ts ts;
static void __iomem *base_addr;
static inline void s3c2410_ts_connect(void)
{
s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_XMON);
s3c2410_gpio_cfgpin(S3C2410_GPG13, S3C2410_GPG13_nXPON);
s3c2410_gpio_cfgpin(S3C2410_GPG14, S3C2410_GPG14_YMON);
s3c2410_gpio_cfgpin(S3C2410_GPG15, S3C2410_GPG15_nYPON);
}
static void touch_timer_fire(unsigned long data)
{
unsigned long data0;
unsigned long data1;
int updown;
data0 = readl(base_addr+S3C2410_ADCDAT0);
data1 = readl(base_addr+S3C2410_ADCDAT1);
updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 &
S3C2410_ADCDAT0_UPDOWN));
if (updown) {
if (ts.count != 0) {
ts.xp >>= ts.shift;
ts.yp >>= ts.shift;
#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
{
struct timeval tv;
do_gettimeofday(&tv);
printk(DEBUG_LVL "T: %06d, X: %03ld, Y: %03ld\n",
(int)tv.tv_usec, ts.xp, ts.yp);
}
#endif
#if 0
if (touch_pen_filtering(&ts.xp, &ts.yp)) { //CoAsia added
input_report_abs(ts.dev, ABS_X, ts.xp); //CoAsia added
input_report_abs(ts.dev, ABS_Y, ts.yp); //CoAsia added
input_report_key(ts.dev, BTN_TOUCH, 1); //CoAsia added
input_report_abs(ts.dev, ABS_PRESSURE, 1); //CoAsia added
input_sync(ts.dev); //CoAsia added
} //CoAsia added
#endif
input_report_abs(ts.dev, ABS_X, ts.xp);
input_report_abs(ts.dev, ABS_Y, ts.yp);
input_report_key(ts.dev, BTN_TOUCH, 1);
input_report_abs(ts.dev, ABS_PRESSURE, 1);
input_sync(ts.dev);
}
ts.xp = 0;
ts.yp = 0;
ts.count = 0;
writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,
base_addr+S3C2410_ADCTSC);
writel(readl(base_addr+S3C2410_ADCCON) |
S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
} else {
ts.count = 0;
input_report_key(ts.dev, BTN_TOUCH, 0);
input_report_abs(ts.dev, ABS_PRESSURE, 0);
input_sync(ts.dev);
writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
}
}
static struct timer_list touch_timer =
TIMER_INITIALIZER(touch_timer_fire, 0, 0);
static irqreturn_t stylus_updown(int irq, void *dev_id)
{
unsigned long data0;
unsigned long data1;
int updown;
data0 = readl(base_addr+S3C2410_ADCDAT0);
data1 = readl(base_addr+S3C2410_ADCDAT1);
updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 &
S3C2410_ADCDAT0_UPDOWN));
/* TODO we should never get an interrupt with updown set while
* the timer is running, but maybe we ought to verify that the
* timer isn't running anyways. */
if (updown)
touch_timer_fire(0);
return IRQ_HANDLED;
}
static irqreturn_t stylus_action(int irq, void *dev_id)
{
unsigned long data0;
unsigned long data1;
data0 = readl(base_addr+S3C2410_ADCDAT0);
data1 = readl(base_addr+S3C2410_ADCDAT1);
ts.xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;
ts.yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;
ts.count++;
if (ts.count < (1<<ts.shift)) {
writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,
base_addr+S3C2410_ADCTSC);
writel(readl(base_addr+S3C2410_ADCCON) |
S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
} else {
mod_timer(&touch_timer, jiffies+1);
writel(WAIT4INT(1), base_addr+S3C2410_ADCTSC);
}
return IRQ_HANDLED;
}
static struct clk *adc_clock;
/*
* The functions for inserting/removing us as a module.
*/
static int __init s3c2410ts_probe(struct platform_device *pdev)
{
int rc;
struct s3c2410_ts_mach_info *info;
struct input_dev *input_dev;
info = ( struct s3c2410_ts_mach_info *)pdev->dev.platform_data;
if (!info)
{
printk(KERN_ERR "Hm... too bad : no platform data for ts\n");
return -EINVAL;
}
#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
printk(DEBUG_LVL "Entering s3c2410ts_init\n");
#endif
adc_clock = clk_get(NULL, "adc");
if (!adc_clock) {
printk(KERN_ERR "failed to get adc clock source\n");
return -ENOENT;
}
clk_enable(adc_clock);
#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
printk(DEBUG_LVL "got and enabled clock\n");
#endif
base_addr = ioremap(S3C2410_PA_ADC,0x20);
if (base_addr == NULL) {
printk(KERN_ERR "Failed to remap register block\n");
return -ENOMEM;
}
/* If we acutally are a S3C2410: Configure GPIOs */
if (!strcmp(pdev->name, "s3c2410-ts"))
s3c2410_ts_connect();
if ((info->presc&0xff) > 0)
writel(S3C2410_ADCCON_PRSCEN |
S3C2410_ADCCON_PRSCVL(info->presc&0xFF),\
base_addr+S3C2410_ADCCON);
else
writel(0,base_addr+S3C2410_ADCCON);
/* Initialise registers */
if ((info->delay&0xffff) > 0)
writel(info->delay & 0xffff, base_addr+S3C2410_ADCDLY);
writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
/* Initialise input stuff */
memset(&ts, 0, sizeof(struct s3c2410ts));
input_dev = input_allocate_device();
if (!input_dev) {
printk(KERN_ERR "Unable to allocate the input device !!\n");
return -ENOMEM;
}
ts.dev = input_dev;
ts.dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) |
BIT_MASK(EV_ABS);
ts.dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
input_set_abs_params(ts.dev, ABS_X, 0, 0x3FF, 0, 0);
input_set_abs_params(ts.dev, ABS_Y, 0, 0x3FF, 0, 0);
input_set_abs_params(ts.dev, ABS_PRESSURE, 0, 1, 0, 0);
//ts.dev->private = &ts;
ts.dev->name = s3c2410ts_name;
ts.dev->id.bustype = BUS_RS232;
ts.dev->id.vendor = 0xDEAD;
ts.dev->id.product = 0xBEEF;
ts.dev->id.version = S3C2410TSVERSION;
ts.shift = info->oversampling_shift;
/* Get irqs */
if (request_irq(IRQ_ADC, stylus_action, IRQF_SAMPLE_RANDOM,
"s3c2410_action", ts.dev)) {
printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_ADC !\n");
iounmap(base_addr);
return -EIO;
}
if (request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM,
"s3c2410_action", ts.dev)) {
printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_TC !\n");
free_irq(IRQ_ADC, ts.dev);
iounmap(base_addr);
return -EIO;
}
printk(KERN_INFO "%s successfully loaded\n", s3c2410ts_name);
/* All went ok, so register to the input system */
rc = input_register_device(ts.dev);
if (rc) {
free_irq(IRQ_TC, ts.dev);
free_irq(IRQ_ADC, ts.dev);
clk_disable(adc_clock);
iounmap(base_addr);
return -EIO;
}
return 0;
}
static int s3c2410ts_remove(struct platform_device *pdev)
{
disable_irq(IRQ_ADC);
disable_irq(IRQ_TC);
free_irq(IRQ_TC,ts.dev);
free_irq(IRQ_ADC,ts.dev);
if (adc_clock) {
clk_disable(adc_clock);
clk_put(adc_clock);
adc_clock = NULL;
}
input_unregister_device(ts.dev);
iounmap(base_addr);
return 0;
}
#ifdef CONFIG_PM
static int s3c2410ts_suspend(struct platform_device *pdev, pm_message_t state)
{
writel(TSC_SLEEP, base_addr+S3C2410_ADCTSC);
writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_STDBM,
base_addr+S3C2410_ADCCON);
disable_irq(IRQ_ADC);
disable_irq(IRQ_TC);
clk_disable(adc_clock);
return 0;
}
static int s3c2410ts_resume(struct platform_device *pdev)
{
struct s3c2410_ts_mach_info *info =
( struct s3c2410_ts_mach_info *)pdev->dev.platform_data;
clk_enable(adc_clock);
msleep(1);
enable_irq(IRQ_ADC);
enable_irq(IRQ_TC);
if ((info->presc&0xff) > 0)
writel(S3C2410_ADCCON_PRSCEN |
S3C2410_ADCCON_PRSCVL(info->presc&0xFF),\
base_addr+S3C2410_ADCCON);
else
write