GPIO驅動相關函數及物理地址,虛擬地址映射

micro2440採用S3C2440處理器,和這個平臺相關的代碼主要在arch/arm/mach-s3c2410和include/asm-arm/arch-s3c2410中,相關驅動在drivers目錄中。
(1)DM9000 網卡驅動
Linux-2.6.32.2/drivers/net/dm9000.c
(2)串口(包括三個串口驅動0,1,2,對應設備名/dev/ttySAC0,1,2)
Linux-2.6.32.2/drivers/serial/s3c2440.c
(3)實時時鐘RTC 驅動
Linux-2.6.32.2/drivers/rtc/rtc-s3c.c
(4)LED 驅動
Linux-2.6.32.2/drivers/char/mini2440_leds.c
(5)按鍵驅動
Linux-2.6.32.2/drivers/char/mini2440_buttons.c
(6)觸摸屏驅動
Linux-2.6.32.2/drivers/input/touchscreen/s3c2410_ts.c
(7)yaffs2 文件系統源代碼目錄
Linux-2.6.32.2/fs/yaffs2
(8)USB 鼠標、鍵盤源代碼
Linux-2.6.32.2/drivers/usb/hid
(9)SD/MMC 卡驅動源代碼目錄(支持高速最大容量32G SD 卡)
Linux-2.6.32.2/drivers/mmc
(10)Nand Flash 驅動
Linux-2.6.32.2/drivers/mtd/nand
(11)UDA1341 音頻驅動目錄
Linux-2.6.32.2/sound/soc/s3c24xx
(12)LCD 驅動
Linux-2.6.32.2/drivers/video/s3c2410fb.c
(13)優盤支持驅動
Linux-2.6.32.2/drivers/usb/storage
(14)萬能USB 攝像頭驅動
Linux-2.6.32.2/drivers/media/video/gspca
(15)I2C-EEPROM 驅動
inux-2.6.32.2/drivers/i2c
(16)背光驅動
Linux-2.6.32.2/drivers/video/mini2440_backlight.c
(17)PWM 控制蜂鳴器驅動
Linux-2.6.32.2/drivers/char/mini2440_pwm.c
(18)看門狗驅動
Linux-2.6.32.2/drivers/watchdog/s3c2410_wdt.c
(19)AD 轉換驅動
Linux-2.6.32.2/drivers/char/mini2440_ad.c
(20)CMOS 攝像頭驅動
Linux-2.6.32.2/drivers/media/video/s3c2440camif.c
(21)USB 無線網卡驅動(型號:TL-WN321G+)
Linux-2.6.32.2/drivers/net/wireless/rt2x00
(22)USB 轉串口驅動
Linux-2.6.32.2/drivers/usb/serial/pl2302.c

1.S3C2410_GPB5是端口編號,定義在regs-gpio.h中,

搜索所在位置:

[root@localhost linux-2.6.32.2_fa]# find ./ -name "regs-gpio.h" 
./arch/arm/mach-s3c2410/include/mach/regs-gpio.h

#define S3C2410_GPIO_BANKB   (32*1)
#define S3C2410_GPIONO(bank,offset)   ((bank) + (offset))
#define S3C2410_GPB5         S3C2410_GPIONO(S3C2410_GPIO_BANKB, 5)

上面的S3C2410_GPB5就是GPIO的編號,也就是在號碼空間(0~32*9-1)中的位置,bank是分組的基號碼,offset是組內偏移量。

2.S3C2410_GPB5_OUTP是端口功能,定義在regs-gpio.h中,
#define S3C2410_GPB5_INP     (0x00 << 10)
#define S3C2410_GPB5_OUTP    (0x01 << 10)
GPBCON的第10、11兩位用於配置GPB5的功能,00 = Input ,01 = Output
3.S3C2410 GPIO的操作函數
在hardware.h文件中有:
s3c2410_gpio_cfgpin     //配置端口的GPIO的功能,輸入,輸出等
s3c2410_gpio_getcfg     //讀取功能配置
s3c2410_gpio_pullup     //配置上拉電阻
s3c2410_modify_misccr //雜項配置

s3c2410_gpio_getirq      //給定端口,轉換出IRQ號
s3c2410_gpio_irqfilter    //配置IRQ過濾使能與否

s3c2410_gpio_setpin     //寫數據到端口,寫0,或者寫1
s3c2410_gpio_getpin     //從端口讀數據

這些函數的實現在gpio.h中

void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)
{
void __iomem *base = S3C2410_GPIO_BASE(pin);//虛擬基地址
unsigned long offs = S3C2410_GPIO_OFFSET(pin);//偏移量
unsigned long flags;
unsigned long dat;

local_irq_save(flags);
dat = __raw_readl(base + 0x04);
dat &= ~(1 << offs);
dat |= to << offs;
__raw_writel(dat, base + 0x04);
local_irq_restore(flags);
}

(自己的理解:dat = __raw_readl(base + 0x04); dat &= ~(1 << offs); dat |= to << offs;譬如操作的是端口5,這幾句能保證其他的端口保持原值,而僅僅是端口5值改變)
4.S3C2410_GPIO_BASE和S3C2410_GPIO_OFFSET也是在regs-gpio.h文件中定義,
#define S3C2410_GPIO_BASE(pin)       ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)
#define S3C2410_GPIO_OFFSET(pin)   ((pin) & 31)

而在map.h中有:

#define S3C24XX_VA_GPIO    S3C2410_ADDR(0x00E00000) //虛擬地址S3C24XX_VA_GPIO= 0xF0E00000
#define S3C2400_PA_GPIO    (0x15600000)
#define S3C2410_PA_GPIO    (0x56000000) //GPACON 物理地址
#define S3C24XX_SZ_GPIO    SZ_1M //0x100000 = 1024 *1024

S3C2410_GPIO_BASE作用是:根據端口編號pin,算出端口所在組的虛擬基址。((pin) & ~31)是去掉pin當中小於等於31的零頭(清0低5位),>>1的原因是每組GPIO中最多可以有32個端口,控制這些端口需要4個寄存器空間,4個寄存器空間就需要4*4=16個字節進行編址,32/16=2,左移一位剛好滿足。也就是說,上一組端口和下一組端口的編號相差32,而控制寄存器的地址相差16。(自己的理解:因爲每個GPIO口對應4個寄存器,每個寄存器32位,S3C2410_GPIO_BASE僅僅是算出虛擬基址,而不管是哪個具體端口,2的5次方正好是32,((pin) & ~31)可以屏蔽掉低五位,右移一位是因爲端口數不超過32,每個GPIO口對應4個寄存器,每個寄存器32位,4*32/8=16=0x10,例如GPIOA=0X10+S3C24XX_VA_GPIO ,GPIOB=0X20+S3C24XX_VA_GPIO,0x10所表示的數的個數超過32)

S3C2410_GPIO_OFFSET作用是:根據端口編號pin,算出端口所在組的偏移量。((pin) & 31)即去掉比31大的數(清0第6位以上的位)。
5. __raw_readl和__raw_writel

Linux對I/O的操作都定義在asm/io.h中,相應的在arm平臺下,就在asm-arm/io.h中。

#define __raw_readl(a)   (__chk_io_ptr(a), *(volatile unsigned int __force   *)(a))
#define __raw_writel(v,a) (__chk_io_ptr(a), *(volatile unsigned int __force   *)(a) = (v))

在include\linux\compiler.h中:
#ifdef __CHECKER__
……
extern void __chk_io_ptr(void __iomem *);
#else
……
# define __chk_io_ptr(x) (void)0
……
#endif

__raw_readl(a)展開是:((void)0, *(volatile unsigned int _force *)(a))。在定義了__CHECKER__的時候先調用__chk_io_ptr檢查該地址,否則__chk_io_ptr什麼也不做,*(volatile unsigned int _force *)(a)就是返回地址爲a處的值。(void)xx的做法有時候是有用的,例如編譯器打開了檢查未使用的參數的時候需要將沒有用到的參數這麼弄一下才能編譯通過。
CPU對I/O的物理地址的編程方式有兩種:一種是I/O映射,一種是內存映射。__raw_readl和__raw_writel等是原始的操作I/O的方法,由此派生出來的操作方法有:inb、outb、_memcpy_fromio、readb、writeb、ioread8、iowrite8等。
6.local_irq_save和local_irq_restore
關中斷和開中斷,在asm-arm/system.h
中定義。
#define local_irq_save(x)      \
({        \
__asm__ __volatile__(      \
"mrs %0, cpsr   @ local_irq_save\n" \
"cpsid i"       \
: "=r" (x) : : "memory", "cc");     \
})

#define local_irq_save(x)      \
({        \
   unsigned long temp;     \
   (void) (&temp == &x);     \
__asm__ __volatile__(      \
"mrs %0, cpsr   @ local_irq_save\n" \
" orr %1, %0, #128\n"      \
" msr cpsr_c, %1"      \
: "=r" (x), "=r" (temp)      \
:        \
: "memory", "cc");      \
})


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章