嵌入式Linux之我行,主要讲述和总结了本人在学习嵌入式linux中的每个步骤。一为总结经验,二希望能给想入门嵌入式Linux的朋友提供方便。如有错误之处,谢请指正。
一、移植环境
- 主 机:VMWare--Fedora 9
- 开发板:Mini2440--64MB Nand
- 编译器:arm-linux-gcc-4.3.2
二、移植步骤
1. 准备驱动源码。因为linux-2.6.30.4内核中没有提供合适的ADC驱动和触摸屏驱动,所以这里就直接用友善提供的驱动
s3c24xx-adc.h
#
ifndef
_S3C2410_ADC_H_
#
define
_S3C2410_ADC_H_
#
define
ADC_WRITE(
ch,
prescale)
(
(
ch)
<
<
16|
(
prescale)
)
#
define
ADC_WRITE_GETCH(
data)
(
(
(
data)
>
>
16)
&
0x7)
#
define
ADC_WRITE_GETPRE(
data)
(
(
data)
&
0xff)
#
endif
/* _S3C2410_ADC_H_ */
mini2440_adc.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/
clk.
h>
#
include
<
asm
/
io.
h>
#
include
<
asm
/
irq.
h>
#
include
<
asm
/
uaccess.
h>
#
include
<
mach/
regs-
clock
.
h>
#
include
<
plat/
regs-
timer.
h>
#
include
<
plat/
regs-
adc.
h>
#
include
<
mach/
regs-
gpio.
h>
#
include
<
linux/
cdev.
h>
#
include
<
linux/
miscdevice.
h>
#
include
"s3c24xx-adc.h"
#
undef
DEBUG
//#define DEBUG
#
ifdef
DEBUG
#
define
DPRINTK(
x.
.
.
)
{
printk(
__FUNCTION__
"(%d): "
,
__LINE__
)
;
printk(
#
#
x)
;
}
#
else
#
define
DPRINTK(
x.
.
.
)
(
void
)
(
0)
#
endif
#
define
DEVICE_NAME "adc"
static
void
__iomem *
base_addr;
typedef
struct
{
wait_queue_head_t wait;
int
channel;
int
prescale;
}
ADC_DEV;
DECLARE_MUTEX(
ADC_LOCK)
;
static
int
OwnADC =
0;
static
ADC_DEV adcdev;
static
volatile
int
ev_adc =
0;
static
int
adc_data;
static
struct
clk *
adc_clock;
#
define
ADCCON (
*
(
volatile
unsigned
long
*
)
(
base_addr +
S3C2410_ADCCON)
)
//ADC control
#
define
ADCTSC (
*
(
volatile
unsigned
long
*
)
(
base_addr +
S3C2410_ADCTSC)
)
//ADC touch screen control
#
define
ADCDLY (
*
(
volatile
unsigned
long
*
)
(
base_addr +
S3C2410_ADCDLY)
)
//ADC start or Interval Delay
#
define
ADCDAT0 (
*
(
volatile
unsigned
long
*
)
(
base_addr +
S3C2410_ADCDAT0)
)
//ADC conversion data 0
#
define
ADCDAT1 (
*
(
volatile
unsigned
long
*
)
(
base_addr +
S3C2410_ADCDAT1)
)
//ADC conversion data 1
#
define
ADCUPDN (
*
(
volatile
unsigned
long
*
)
(
base_addr +
0x14)
)
//Stylus Up/Down interrupt status
#
define
PRESCALE_DIS (
0 <
<
14)
#
define
PRESCALE_EN (
1 <
<
14)
#
define
PRSCVL(
x)
(
(
x)
<
<
6)
#
define
ADC_INPUT(
x)
(
(
x)
<
<
3)
#
define
ADC_START (
1 <
<
0)
#
define
ADC_ENDCVT (
1 <
<
15)
#
define
START_ADC_AIN(
ch,
prescale)
/
do
{
/
ADCCON =
PRESCALE_EN |
PRSCVL(
prescale)
|
ADC_INPUT(
(
ch)
)
;
/
ADCCON |
=
ADC_START;
/
}
while
(
0)
static
irqreturn_t adcdone_int_handler(
int
irq,
void
*
dev_id)
{
if
(
OwnADC)
{
adc_data =
ADCDAT0 &
0x3ff;
ev_adc =
1;
wake_up_interruptible(
&
adcdev.
wait)
;
}
return
IRQ_HANDLED;
}
static
ssize_t s3c2410_adc_read(
struct
file
*
filp,
char
*
buffer,
size_t
count
,
loff_t *
ppos)
{
char
str[
20]
;
int
value;
size_t
len;
if
(
down_trylock(
&
ADC_LOCK)
=
=
0)
{
OwnADC =
1;
START_ADC_AIN(
adcdev.
channel,
adcdev.
prescale)
;
wait_event_interruptible(
adcdev.
wait,
ev_adc)
;
ev_adc =
0;
DPRINTK(
"AIN[%d] = 0x%04x, %d/n"
,
adcdev.
channel,
adc_data,
ADCCON &
0x80 ?
1:
0)
;
value =
adc_data;
sprintf
(
str,
"%5d"
,
adc_data)
;
copy_to_user(
buffer,
(
char
*
)
&
adc_data,
sizeof
(
adc_data)
)
;
OwnADC =
0;
up(
&
ADC_LOCK)
;
}
else
{
value =
-
1;
}
len =
sprintf
(
str,
"%d/n"
,
value)
;
if
(
count
>
=
len)
{
int
r =
copy_to_user(
buffer,
str,
len)
;
return
r ?
r :
len;
}
else
{
return
-
EINVAL;
}
}
static
int
s3c2410_adc_open(
struct
inode *
inode,
struct
file
*
filp)
{
init_waitqueue_head(
&
(
adcdev.
wait)
)
;
adcdev.
channel=
0;
adcdev.
prescale=
0xff;
DPRINTK(
"adc opened/n"
)
;
return
0;
}
static
int
s3c2410_adc_release(
struct
inode *
inode,
struct
file
*
filp)
{
DPRINTK(
"adc closed/n"
)
;
return
0;
}
static
struct
file_operations dev_fops =
{
owner:
THIS_MODULE,
open
:
s3c2410_adc_open,
read
:
s3c2410_adc_read,
release:
s3c2410_adc_release,
}
;
static
struct
miscdevice misc =
{
.
minor =
MISC_DYNAMIC_MINOR,
.
name =
DEVICE_NAME,
.
fops =
&
dev_fops,
}
;
static
int
__init dev_init(
void
)
{
int
ret;
base_addr=
ioremap(
S3C2410_PA_ADC,
0x20)
;
if
(
base_addr =
=
NULL
)
{
printk(
KERN_ERR "Failed to remap register block/n"
)
;
return
-
ENOMEM;
}
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)
;
/* normal ADC */
ADCTSC =
0;
ret =
request_irq(
IRQ_ADC,
adcdone_int_handler,
IRQF_SHARED,
DEVICE_NAME,
&
adcdev)
;
if
(
ret)
{
iounmap(
base_addr)
;
return
ret;
}
ret =
misc_register(
&
misc)
;
printk (
DEVICE_NAME"/tinitialized/n"
)
;
return
ret;
}
static
void
__exit dev_exit(
void
)
{
free_irq(
IRQ_ADC,
&
adcdev)
;
iounmap(
base_addr)
;
if
(
adc_clock)
{
clk_disable(
adc_clock)
;
clk_put(
adc_clock)
;
adc_clock =
NULL
;
}
misc_deregister(
&
misc)
;
}
EXPORT_SYMBOL(
ADC_LOCK)
;
module_init(
dev_init)
;
module_exit(
dev_exit)
;
MODULE_LICENSE(
"GPL"
)
;
MODULE_AUTHOR(
"FriendlyARM Inc."
)
;
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
<
plat/
regs-
adc.
h>
#
include
<
mach/
regs-
gpio.
h>
/* For ts.dev.id.version */
#
define
S3C2410TSVERSION 0x0101
#
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)
)
static
char
*
s3c2410ts_name =
"s3c2410 TouchScreen"
;
static
struct
input_dev *
dev;
static
long
xp;
static
long
yp;
static
int
count
;
extern
struct
semaphore ADC_LOCK;
static
int
OwnADC =
0;
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 =
ioread32(
base_addr+
S3C2410_ADCDAT0)
;
data1 =
ioread32(
base_addr+
S3C2410_ADCDAT1)
;
updown =
(
!
(
data0 &
S3C2410_ADCDAT0_UPDOWN)
)
&
&
(
!
(
data1 &
S3C2410_ADCDAT0_UPDOWN)
)
;
if
(
updown)
{
if
(
count
!
=
0)
{
long
tmp;
tmp =
xp;
xp =
yp;
yp =
tmp;
xp >
>
=
2;
yp >
>
=
2;
#
ifdef
CONFIG_TOUCHSCREEN_MY2440_DEBUG
struct
timeval
tv;
do_gettimeofday(
&
tv)
;
printk(
KERN_DEBUG "T: %06d, X: %03ld, Y: %03ld/n"
,
(
int
)
tv.
tv_usec,
xp,
yp)
;
#
endif
input_report_abs(
dev,
ABS_X,
xp)
;
input_report_abs(
dev,
ABS_Y,
yp)
;
input_report_key(
dev,
BTN_TOUCH,
1)
;
input_report_abs(
dev,
ABS_PRESSURE,
1)
;
input_sync(
dev)
;
}
xp =
0;
yp =
0;
count
=
0;
iowrite32(
S3C2410_ADCTSC_PULL_UP_DISABLE |
AUTOPST,
base_addr+
S3C2410_ADCTSC)
;
iowrite32(
ioread32(
base_addr+
S3C2410_ADCCON)
|
S3C2410_ADCCON_ENABLE_START,
base_addr+
S3C2410_ADCCON)
;
}
else
{
count
=
0;
input_report_key(
dev,
BTN_TOUCH,
0)
;
input_report_abs(
dev,
ABS_PRESSURE,
0)
;
input_sync(
dev)
;
iowrite32(
WAIT4INT(
0)
,
base_addr+
S3C2410_ADCTSC)
;
if
(
OwnADC)
{
OwnADC =
0;
up(
&
ADC_LOCK)
;
}
}
}
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;
if
(
down_trylock(
&
ADC_LOCK)
=
=
0)
{
OwnADC =
1;
data0 =
ioread32(
base_addr+
S3C2410_ADCDAT0)
;
data1 =
ioread32(
base_addr+
S3C2410_ADCDAT1)
;
updown =
(
!
(
data0 &
S3C2410_ADCDAT0_UPDOWN)
)
&
&
(
!
(
data1 &
S3C2410_ADCDAT0_UPDOWN)
)
;
if
(
updown)
{
touch_timer_fire(
0)
;
}
else
{
OwnADC =
0;
up(
&
ADC_LOCK)
;
}
}
return
IRQ_HANDLED;
}
static
irqreturn_t stylus_action(
int
irq,
void
*
dev_id)
{
unsigned
long
data0;
unsigned
long
data1;
if
(
OwnADC)
{
data0 =
ioread32(
base_addr+
S3C2410_ADCDAT0)
;
data1 =
ioread32(
base_addr+
S3C2410_ADCDAT1)
;
xp +
=
data0 &
S3C2410_ADCDAT0_XPDATA_MASK;
yp +
=
data1 &
S3C2410_ADCDAT1_YPDATA_MASK;
count
+
+
;
if
(
count
<
(
1<
<
2)
)
{
iowrite32(
S3C2410_ADCTSC_PULL_UP_DISABLE |
AUTOPST,
base_addr+
S3C2410_ADCTSC)
;
iowrite32(
ioread32(
base_addr+
S3C2410_ADCCON)
|
S3C2410_ADCCON_ENABLE_START,
base_addr+
S3C2410_ADCCON)
;
}
else
{
mod_timer(
&
touch_timer,
jiffies+
1)
;
iowrite32(
WAIT4INT(
1)
,
base_addr+
S3C2410_ADCTSC)
;
}
}
return
IRQ_HANDLED;
}
static
struct
clk *
adc_clock;
static
int
__init s3c2410ts_init(
void
)
{
struct
input_dev *
input_dev;
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)
;
base_addr=
ioremap(
S3C2410_PA_ADC,
0x20)
;
if
(
base_addr =
=
NULL
)
{
printk(
KERN_ERR "Failed to remap register block/n"
)
;
return
-
ENOMEM;
}
/* Configure GPIOs */
s3c2410_ts_connect(
)
;
iowrite32(
S3C2410_ADCCON_PRSCEN |
S3C2410_ADCCON_PRSCVL(
0xFF)
,
/
base_addr+
S3C2410_ADCCON)