2012年9月2日 星期日

如何撰寫在Linux Kernel 2.6.x的GPIO驅動程式


前言
GPIO是目前SoC常常會使用到的一個元件,以往的GPIO使用上並不普遍,所以在Linux Kernel並未加以統一寫法,因此以往的寫法則各家自行撰寫,比較多的是架構在misc的驅動之下,但今日新的Linux Kernel則有給予新的統一做法,它讓kernel中有要用GPIO的其它驅動程式可以透過其定義的library API來使用,而GPIO本身則實現其實體層的控制就好,如此一來其它使用GPIO的驅動則在porting更容易了。

什麼是GPIO
對於GPIO我們要先有一些了解,GPIO – Generic Program Input Output,中文說法是可程式輸出輸入的控制訊號,意思是該點可以設定為input或者output訊號,而其輸出的訊號則是單純的數位訊號,不是high就是low,而輸入訊號的偵測也是一樣,而其電位則是TTL訊號,而這樣的東西,通常可以例如模擬I2C HOST,或者拿來讀取RTC的介面,又或接一個LED當做某些事件的指示使用。

Kernel中的GPIO Library
還没有寫驅動程式之前,我們先來了解這個library,它是最近的Linux kernel版本所定義的,是給其它需要用到GPIO的驅動程式所使用,意思就是在Kernel內部定義了義標準的GPIO Library API,如此一來對需要使用到GPIO的驅動程式便可以不用理會其底層的GPIO,也不會因平台不同而各家自行所定義的GPIO API而造成上層的驅動程式而跟著修改,我們可以用以下的軟體加構圖來了解其中的做法:












目前定義較常用的Kernel API有如下:
  • int gpio_direction_output(unsigned gpio, int value);
這是設定該GPIO為output,第一個參數為GPIO number,第二個參數則是設定其為輸出時的初始值。
  • int gpio_set_value(unsigned gpio, int value);
這是針對己經設為output的GPIO設定其值為high或者為low。
  • int gpio_direction_input(unsigned gpio);
這是設定該GPIO為input。
  • int gpio_get_value(unsigned gpio);
這是針對己設為input的GPIO讀回其現在的輸入值為何。
  • int gpio_request_array(struct gpio *array, size_t num);
這是若要使用任何GPIO時,必須先呼叫該API,這個動作有點類似open的動作,以免其它人會佔用到相同的GPIO pin,這個API可以讓你要求不連續任意個存在的GPIO,之後你就可以使用以上的所有API。
struct gpio {
        unsigned        gpio;
        unsigned long   flags;
        const char      *label;
};
flags可以設定的值如下:
#define GPIOF_DIR_OUT   (0 << 0)
#define GPIOF_DIR_IN    (1 << 0)

#define GPIOF_INIT_LOW  (0 << 1)
#define GPIOF_INIT_HIGH (1 << 1)

#define GPIOF_IN             (GPIOF_DIR_IN)
#define GPIOF_OUT_INIT_LOW (GPIOF_DIR_OUT | GPIOF_INIT_LOW)
#define GPIOF_OUT_INIT_HIGH (GPIOF_DIR_OUT | GPIOF_INIT_HIGH)
  • int gpio_free_array(struct gpio *array, size_t num);
上一個若類似open的動作,這一個API就是close的動作。
以上這些kernel API都是給kernel內部其它driver使用的,使用它之前必需確定GPIO驅動已經被載入,否則將會無法使用,所以這時驅動載入的順序是非常重要的。另外由於有這一層的介面,使得其它會使用到GPIO的驅動程式,會統一的介面,不會因為底層的GPIO控制驅動程式不同因而有不同的介面,而產生需要修改程式,這對軟體的維護會是一大負擔。

實體層的GPIO chip controller的驅動程式
這是一個底層的驅動程式,直接控制GPIO的晶片,並接受上層的管理層的指令動作,並以此提供其它需要使用GPIO的驅動程式,可以有統一的介面。該驅動程式會用到一個資料結構,它是宣告在include/asm-generic/gpio.h,並且在kernel中必需將CONFIG_GENERIC_GPIO啓動,其宣告如下:
/**
 * 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 code
 *      will be used when this is omitted, but custom code 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
 * @names: if set, must be an array of strings to use as alternative
 *      names for the GPIOs in this chip. Any entry in the array
 *      may be NULL if there is no alias for the GPIO, however the
 *      array must be @ngpio entries long.  A name can include a single printk
 *      format specifier for an unsigned int.  It is substituted by the actual
 *      number of the gpio.
 *
 * 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);
        int                     (*set_debounce)(struct gpio_chip *chip,
                                                unsigned offset, unsigned debounce);

        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;
        const char              *const *names;
        unsigned                can_sleep:1;
        unsigned                exported:1;

#if defined(CONFIG_OF_GPIO)
        /*
         * If CONFIG_OF is enabled, then all GPIO controllers described in the
         * device tree automatically may have an OF translation
         */
        struct device_node *of_node;
        int of_gpio_n_cells;
        int (*of_xlate)(struct gpio_chip *gc, struct device_node *np,
                        const void *gpio_spec, u32 *flags);
#endif
};
另外向上註冊及移除的API如下:
/* add/remove chips */
extern int gpiochip_add(struct gpio_chip *chip);
extern int __must_check gpiochip_remove(struct gpio_chip *chip);
extern struct gpio_chip *gpiochip_find(void *data, int (*match)(struct gpio_chip *chip, void *data));

程式範例
GPIO控制晶片驅動程式:
struct myart_gpio_module {
struct gpio_chip gpio;
……………………………….
};

int myart_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
struct myart_gpio_module *module;

module = container_of(chip, struct myart_gpio_module, gpio);
return module->intr_start + offset;

}

int myart_gpio_direction_input(struct gpio_chip *chip,unsigned offset)
{
myart_gpio_inout_pin(offset , MCPU_GPIO_INPUT);
return 0;
}

int myart_gpio_get_value(struct gpio_chip *chip, unsigned offset)
{
return myart_gpio_get_pin(offset);
}

int myart_gpio_direction_output(struct gpio_chip *chip,unsigned offset, int value)
{
myart_gpio_inout_pin(offset , MCPU_GPIO_OUTPUT);
return 0;
}

void myart_gpio_set_value(struct gpio_chip *chip, unsigned offset, int value)
{
myart_gpio_set_pin(offset , value);
}

static int __devinit myart_gpio_probe(struct platform_device *pdev)
{
……………………………….
/* Initialize the GPIO data structures */
module->gpio.dev = &pdev->dev;
module->gpio.label = pdev->name;
module->gpio.owner = THIS_MODULE;
module->gpio.direction_input = myart_gpio_direction_input;
module->gpio.get = myart_gpio_get_value;
module->gpio.direction_output = myart_gpio_direction_output;
module->gpio.set = myart_gpio_set_value;
module->gpio.to_irq = myart_gpio_to_irq;
module->gpio.can_sleep = 0;
module->gpio.base = 0;
module->gpio.ngpio = MCPU_GPIO_NUM;
ret = gpiochip_add(&(module->gpio));
……………………………………..
}

static int __devexit my_gpio_remove(struct platform_device *pdev)
{
……………………………..
ret = gpiochip_remove(&module->gpio);
if (ret) {
dev_err(dev, "unable to remove GPIO chip\n");
return ret;
}
………………………..
return 0;
}

static struct platform_driver my_gpio_driver = {
.driver = {
.name = "GPIO Driver",
.owner = THIS_MODULE,
},
.probe = my_gpio_probe,
.remove = __devexit_p(my_gpio_remove),
};

static int __init my_gpio_module_init(void)
{
return platform_driver_register(&my_gpio_driver);
}

static void __exit my_gpio_module_exit(void)
{
platform_driver_unregister(&my_gpio_driver);
}

module_init(my_gpio_module_init);
module_exit(my_gpio_module_exit);

上層GPIO應用驅動程式
上層的GPIO應用驅程式則是使用GPIO Kernel Library所提供的介面來撰寫驅動程式,如RTC, I2C等等都是常看到的使用GPIO來應用的驅動程式,它必須使用gpio_request_array()來向kernel取得所要用的GPIO,並確定它是没被其它驅動程式所使用,使用後並將其佔住不被其它驅動程式所佔用,在該應用驅動程式下載時需要呼叫gpio_free_array()將所佔用的GPIO free給其它的驅動程式使用,在整個驅動程式中則可以使用gpio_direction_input(), gpio_direction_output(), gpio_set_value(), gpio_get_value()來個別控制相對的GPIO為input or output,並且讀回或者設定相對GPIO為high or low。

程式範例
GPIO RTC驅動程式:
static void rtc_write(u8 cmd, u8 Data)
{
………………………
gpio_direction_output(GPIO_RTC_DATA, MCPU_GPIO_LOW);
    gpio_set_value(GPIO_RTC_RESET, MCPU_GPIO_HIGH);
………………………
}

static u8 rtc_read(u8 cmd)
{
…………………
gpio_direction_input(GPIO_RTC_DATA);
………………..
v = gpio_get_value(GPIO_RTC_DATA);
…………………….
}

static int myart_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
……………………..
v = rtc_read(RTC_MONTH_R);
………………………………
}

static int myart_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
……………………
rtc_write(RTC_YEAR_W, v);
……………………..
}

static const struct rtc_class_ops myart_rtc_ops = {
.read_time = myart_rtc_read_time,
.set_time = myart_rtc_set_time,
};

static struct gpio rtc_gpios[] = {
    { GPIO_RTC_RESET, GPIOF_DIR_OUT, "rtc_reset"}, 
    { GPIO_RTC_SCLK, GPIOF_DIR_OUT, "rtc_sclk"}, 
    { GPIO_RTC_DATA, GPIOF_DIR_OUT, "rtc_data"}, 
};

static int __init myart_rtc_probe(struct platform_device *pdev)
{
…………………………..
err = gpio_request_array(rtc_gpios, ARRAY_SIZE(rtc_gpios));
…………………………..
rtc = rtc_device_register("rtc-myart", &pdev->dev, &myart_rtc_ops,
  THIS_MODULE);
…………………………..
}

static int __exit myart_rtc_remove(struct platform_device *pdev)
{
…………………………..
rtc_device_unregister(rtc);
    gpio_free_array(rtc_gpios, ARRAY_SIZE(rtc_gpios)); 
return 0;
}

static struct platform_driver myart_rtc_driver = {
.driver = {
.name = "RTC", 
.owner = THIS_MODULE,
},
.remove = __exit_p(myart_rtc_remove),
};

static int __init myart_rtc_init(void)
{
return platform_driver_probe(&myart_rtc_driver, myart_rtc_probe);
}

static void __exit myart_rtc_exit(void)
{
    platform_driver_unregister(&myart_rtc_driver);
}

module_init(myart_rtc_init);
module_exit(myart_rtc_exit);