GPIO是與硬體體系密切相關的,linux提供一個模型來讓驅動統一處理GPIO,即各個板卡都有實現自己的gpio_chip控制模組:request, free, input,output, get,set,irq...
然後把控制模組註冊到核心中,這時會改變全域gpio數組:gpio_desc[].
當使用者請求gpio時,就會到這個數組中找到,並調用這個GPIO對應的gpio_chip的處理函數。
gpio實現為一組可用的 gpio_chip, 由驅動傳入對應 gpio的全域序號 去 request, dataout ,datain, free. 這時會調用gpio_chip中具體的實現。
寄存器讀寫函數: __raw_writel() __raw_writeb() __raw_readl() __raw_readb()
gpio是一組可控制項的腳,由多個寄存器同時控制。通過設定對應的寄存器可以達到設定GPIO口對應狀態與功能。
資料狀態,輸入輸出方向,清零,中斷(那個邊沿觸發), 一般是一組(bank)一組的。
//****************linux 中 GPIO模型****************************************//
註冊方法:
1:struct gpio_chip: 表示一個gpio controller.通過這個結構抽象化所有的 GPIO源,而讓板上其它的模組可以用相同的介面調用使用這些GPIO。
2: struct gpio_desc: 表示一個gpio口,含對應的 gpio_chip.
3: ARCH_NR_GPIOS: 與板相關的GPIO口數量,即是全域GPIO數組:static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
4: 註冊 gpio_chip時,就是根據 chip 的資料 修改全域 GPIO數組中 gpio_desc 欄位(chip, flags)。
//************* twl4030 中 GPIO的實現 *******************//
首先 twl4030中的 GPIO模組: 在 twl4030的驅動註冊時,probe中,產生了GPIO對應的 i2c_client 與 platform_device.
twl4030,當載入GPIO的驅動(匹配platform_device)時,首先進行對應的設定,GPIO設定。
然後設定結構: gpio_chip: twl_gpiochip: gpio_twl4030_pulls,pio_twl4030_debounce,
註冊 之:ret = gpiochip_add(&twl_gpiochip);//添加到全域的 gpio_desc數組中。即是設定全域gpio_desc中這個gpio_chip對應的幾個gpio的欄位。
setup之:status = pdata->setup(&pdev->dev, pdata->gpio_base, TWL4030_GPIO_MAX);
setup(...) ==》 twl4030_mmc_init(mmc); //在添加完gpio後,進行探測MMC,然後註冊之。Register MMC devices.
//*************** omap3430的實現 註冊 GPIO ******************//
static int __init _omap_gpio_init(void)
gpio_bank_count: 多少組gpio組。
這裡同時 註冊了對應的中斷處理常式:
bank 主中斷處理:gpio_irq_handler
bank 下的子GPIO全設為:handle_simple_irq。
//註冊函數,即是把根據chip裝置全域GPIO數組的對應gpio的欄位。
int gpiochip_add(struct gpio_chip *chip)
{
for (id = base; id < base + chip->ngpio; id++) {
gpio_desc[id].chip = chip; //設定全域的gpio_desc中的 對應的成員的 chip 為,註冊的這個chip.
gpio_desc[id].flags = !chip->direction_input ? (1 << FLAG_IS_OUT): 0; //及gpio方向。
}
//
"通用調用方法:"
static int __init wl127x_vio_leakage_fix(void)
{
int ret = 0;
ret = gpio_request(WL127X_BTEN_GPIO, "wl127x_bten");
if (ret < 0) {
printk(KERN_ERR "wl127x_bten gpio_%d request fail",
WL127X_BTEN_GPIO);
goto fail;
}
gpio_direction_output(WL127X_BTEN_GPIO, 1);
mdelay(10);
gpio_direction_output(WL127X_BTEN_GPIO, 0);
udelay(64);
gpio_free(WL127X_BTEN_GPIO);
fail:
return ret;
}
//********************//
一. GPIO物理起始地址: 對於要控制GPIO的嵌入式編程人員來講,GPIO的物理地址是必須要搞清楚的。STB02500SOC中GPIO的物理基本地址是:0x4006 0000空間長度是:0x44。(gpio_base = 0x4006 0000 len = 0x44)
二. GPIO寄存器:
GPIO共有 9個寄存器分別是:
1. GPO: GPO(32bit)寄存器用於設定或清除對應的GPIO引腳值(高,或者低)。具體控制時需要先讀出GPIO引腳的值,然後再用”或”,”與”進行相應的邏輯設定和清除操作,很顯然這是非原子的操作,在多任務系統裡,是有可能發生意外的,需要程式員在軟體上實現相應的原子操作,這和ARM系統的 I/O操作相比應該是比較落後的)。
2. GPTC: GPTC(32bit)寄存器用於設定對應的GPIO引腳是輸出還是輸入(這樣比較好理解些)程式設定相應的bit為1對應的GPIO引腳就是輸出否則就是輸入(呵呵,這裡還要聲明一下下,該寄存器要起作用還要設定好GPTS寄存器)。
3. GPOS: GPOS(64bit)寄存器用於控制GPIO相應的引腳是由GPO驅動還是有Alt_Output_x(1,2,3)驅動(就這樣簡單的理解吧,嘿嘿)。
4. GPTS: GPTS(64bit)這個寄存器功能還是比較難說清楚的呀,簡單的講如果需要用GPO驅動GPIO寄存器,就需要設定對應2bit為00選擇GPTC作為輸出使能控制器,這樣GPTC寄存器才能夠起作用的。
5. GPOD: GPOD(32bit)這個寄存器是比較單純的,如果對應位設定1相應的GPIO引腳就處於漏極開路狀態,這樣可以適應多電壓系統,和多輸出驅動單輸入系統。
6. GPI: GPI(32bit) 就是相應GPIO引腳的值(高電平,還是低電平)。
7. GPIS1:我暫時還沒有研究。
8. GPIS2: 我暫時還沒有研究。
9. GPIS3: 我暫時還沒有研究。
注意:這裡要特別聲明:這裡的GPIO0對應GPO的bit0,GPO的bit0實際是32位GPOd的bit31(以ARM系統來看)。
三:
對於在不支援虛擬記憶體的作業系統和根本就沒有使用作業系統的系統裡操作GPIO直接讀寫對應的GPIO寄存器就可以啦,但是在linux這樣的作業系統下,你必須編寫一個操作GPIO的驅動,或者是使用一些變通的技巧來操作GPIO.
目前我所知道的在linux下操作GPIO有兩種方法:
1. 編寫驅動,這當然要熟悉linux下驅動的編寫方法和技巧,在驅動裡可以使用ioremap函數獲得GPIO物理基地址指標,然後使用這個指標根據ioctl命令進行GPIO寄存器的讀寫,並把結果回送到應用程式層.
2. 在應用程式層使用mmap函數在應用程式層獲得GPIO物理基地址對應的虛擬位址指標,然後使用這個指標來讀寫GPIO寄存器
總結:
雖然GPIO寄存器很多但是熟悉後,使用起來也很簡單的,關鍵是要理解透每個GPIO引腳的功能,和各寄存器的功能特點。其實如果只是做簡單的I/O輸入輸出控制(大多數單片機開發人員最常用用到),只要熟悉 GPO,GPI,GPTC就可以啦。
GPIO是一種通用I/O協議,在讀寫的時候,需要對特定寄存器進行一些設定,在規範描述的時候,一般會說GPIO 0-12,GPIO 38-43等說法,GPIO和一般的寄存器讀寫很不一樣,因此它是兩個號碼控制一個狀態。
GPIO的一般讀寫順序為:
1、首先設定需要進行操作的GPIO範圍;
2、在讀/寫操作前,對GPIO的選擇寄存器設定為1/0;
3、讀/寫寄存器(基地址+位移)。
static struct gpio_desc gpio_desc[ARCH_NR_GPIOS]; //全域的gpio_chip 存放數組
static struct gpio_chip twl_gpiochip = {
.label = "twl4030",
.owner = THIS_MODULE,
.request = twl_request,
.free = twl_free,
.direction_input = twl_direction_in,
.get = twl_get,
.direction_output = twl_direction_out,
.set = twl_set,
.to_irq = twl_to_irq,
.can_sleep = 1,
};
/* gpio_lock prevents conflicts during gpio_desc[] table updates.
* While any GPIO is requested, its gpio_chip is not removable;
* each GPIO's "requested" flag serves as a lock and refcount.
*/
struct gpio_desc {
struct gpio_chip *chip;
unsigned long flags;
/* flag symbols are bit numbers */
#define FLAG_REQUESTED 0
#define FLAG_IS_OUT 1
#define FLAG_RESERVED 2
#define FLAG_EXPORT 3 /* protected by sysfs_lock */
#define FLAG_SYSFS 4 /* exported via /sys/class/gpio/control */
#ifdef CONFIG_DEBUG_FS
const char *label;
#endif
};
/**
* struct gpio_chip - abstract a GPIO controller
* @label: for diagnostics
* @dev: optional device providing the GPIOs
* @owner: helps prevent removal of modules exporting active GPIOs
* @request: optional hook for chip-specific activation, such as
* enabling module power and clock; may sleep
* @free: optional hook for chip-specific deactivation, such as
* disabling module power and clock; may sleep
* @direction_input: configures signal "offset" as input, or returns error
* @get: returns value for signal "offset"; for output signals this
* returns either the value actually sensed, or zero
* @direction_output: configures signal "offset" as output, or returns error
* @set: assigns output value for signal "offset"
* @to_irq: optional hook supporting non-static gpio_to_irq() mappings;
* implementation may not sleep
* @dbg_show: optional routine to show contents in debugfs; default co de
* will be used when this is omitted, but custom co de can show extra
* state (such as pullup/pulldown configuration).
* @base: identifies the first GPIO number handled by this chip; or, if
* negative during registration, requests dynamic ID allocation.
* @ngpio: the number of GPIOs handled by this controller; the last GPIO
* handled is (base + ngpio - 1).
* @can_sleep: flag must be set iff get()/set() methods sleep, as they
* must while accessing GPIO expander chips over I2C or SPI
*
* A gpio_chip can help platforms abstract various sources of GPIOs so
* they can all be accessed through a common programing interface.
* Example sources would be SOC controllers, FPGAs, multifunction
* chips, dedicated GPIO expanders, and so on.
*
* Each chip controls a number of signals, identified in method calls
* by "offset" values in the range 0..(@ngpio - 1). When those signals
* are referenced through calls like gpio_get_value(gpio), the offset
* is calculated by subtracting @base from the gpio number.
*/
struct gpio_chip {
const char *label;
struct device *dev;
struct module *owner;
int (*request)(struct gpio_chip *chip,
unsigned offset);
void (*free)(struct gpio_chip *chip,
unsigned offset);
int (*direction_input)(struct gpio_chip *chip,
unsigned offset);
int (*get)(struct gpio_chip *chip,
unsigned offset);
int (*direction_output)(struct gpio_chip *chip,
unsigned offset, int value);
void (*set)(struct gpio_chip *chip,
unsigned offset, int value);
int (*to_irq)(struct gpio_chip *chip,
unsigned offset);
void (*dbg_show)(struct seq_file *s,
struct gpio_chip *chip);
int base;
u16 ngpio;
unsigned can_sleep:1;
unsigned exported:1;
};
本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/cuiyan0214/archive/2010/05/04/5556262.aspx