1. 程式人生 > >15 核心裡leds-gpio裝置驅動的裝置樹方法

15 核心裡leds-gpio裝置驅動的裝置樹方法

在linux核心裡已提供了連線到gpio的led裝置驅動,只需要通過platform_device或裝置提供相應的硬體資源即可.
使用platform_device方法可參考: http://blog.csdn.net/jklinux/article/details/73850470

核心裡的leds-gpio裝置驅動配置選項:

make menuconfig ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu-
  Device Drivers  --->
    -*- LED Support  --->
      <*>   LED Support for
GPIO connected LEDs 通過配置選項的幫助資訊可以查出對應的裝置驅動原始檔: drivers/leds/leds-gpio.c

leds-gpio.c原始檔裡裝置樹相關的主要內容:

274 static struct platform_driver gpio_led_driver = {
275     .probe      = gpio_led_probe,
276     .shutdown   = gpio_led_shutdown,
277     .driver     = {
278         .name   = "leds-gpio",
279
.of_match_table = of_gpio_leds_match, 280 }, 281 }; 223 static const struct of_device_id of_gpio_leds_match[] = { 224 { .compatible = "gpio-leds", }, 225 {}, 226 }; 227 228 MODULE_DEVICE_TABLE(of, of_gpio_leds_match); 這兩部分內容可得知,此裝置驅動是一個platform_driver的物件,可通過platform_device或裝置樹裡的裝置節點來匹配。裝置節點裡的compatible屬性值應是"gpio-leds"
.
匹配上後, 裝置驅動裡gpio_led_probe函式就會被觸發呼叫. 在probe函式裡就會取出裝置樹裡提供的硬體資源.

230 static int gpio_led_probe(struct platform_device *pdev)
231 {
232     struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev); //使用裝置樹的方式, pdata應為NULL;
233     struct gpio_leds_priv *priv;
234     int i, ret = 0;
235 
236     if (pdata && pdata->num_leds) {
        ... //使用platform_device方式的獲取硬體資源的處理程式碼
251     } else {
252         priv = gpio_leds_create(pdev); //使用裝置樹時的處理程式碼
253         if (IS_ERR(priv))
254             return PTR_ERR(priv);
255     }
在gpio_leds_create函式裡獲取裝置節點的資源
156 static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
157 {
158     struct device *dev = &pdev->dev;
159     struct fwnode_handle *child;
160     struct gpio_leds_priv *priv;
161     int count, ret;
162 
163     count = device_get_child_node_count(dev); //獲取子節點的個數, 意味著裝置節點裡是需要包含子節點的,並不是使用裝置節點的屬性來的提供資源.
164     if (!count)
165         return ERR_PTR(-ENODEV);
166 
167     priv = devm_kzalloc(dev, sizeof_gpio_leds_priv(count), GFP_KERNEL);
168     if (!priv)
169         return ERR_PTR(-ENOMEM);
171     device_for_each_child_node(dev, child) { //遍歷裝置節點裡的每一個子節點
172         struct gpio_led_data *led_dat = &priv->leds[priv->num_leds];
173         struct gpio_led led = {};
174         const char *state = NULL;
175         struct device_node *np = to_of_node(child);
176 
177         ret = fwnode_property_read_string(child, "label", &led.name); //獲取子節點的label屬性值. 意味著每個子節點應有一個label屬性,屬性值應為字串.
178         if (ret && IS_ENABLED(CONFIG_OF) && np)
179             led.name = np->name;
180         if (!led.name) {
181             fwnode_handle_put(child);
182             return ERR_PTR(-EINVAL);
183         }
184         //獲取子節點裡的gpio口資訊. con_id為NULL,意味著子節點應是使用gpios屬性來提供led所連線的io口資訊. 而且這裡僅是獲取一個io口資訊,並不是多個io口,意味著每個子節點表示一個led燈的資源
185         led.gpiod = devm_fwnode_get_gpiod_from_child(dev, NULL, child,
186                                  GPIOD_ASIS,
187                                  led.name);
188         if (IS_ERR(led.gpiod)) { //獲取io口失敗則返回錯誤碼
189             fwnode_handle_put(child);
190             return ERR_CAST(led.gpiod);
191         }
192         //下面的屬性應是可選設定的,因並沒有不設值而返回錯誤碼.
193         fwnode_property_read_string(child, "linux,default-trigger",
194                         &led.default_trigger);
196         if (!fwnode_property_read_string(child, "default-state",
197                          &state)) {
198             if (!strcmp(state, "keep"))
199                 led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
200             else if (!strcmp(state, "on"))
201                 led.default_state = LEDS_GPIO_DEFSTATE_ON;
202             else
203                 led.default_state = LEDS_GPIO_DEFSTATE_OFF;
204         }
205 
206         if (fwnode_property_present(child, "retain-state-suspended"))
207             led.retain_state_suspended = 1;
208         if (fwnode_property_present(child, "panic-indicator"))
209             led.panic_indicator = 1;
210 
211         ret = create_gpio_led(&led, led_dat, dev, NULL);
212         if (ret < 0) {
213             fwnode_handle_put(child);
214             return ERR_PTR(ret);
215         }
216         led_dat->cdev.dev->of_node = np;
217         priv->num_leds++;
218     }
219 
220     return priv;
221 }

現板上的STATUS-LED連線到PA17, PWR-LED連線到PL10.
綜上所述,關於leds-gpio裝置驅動的裝置樹可:

myleds {
    compatible = "gpio-leds";

    superled0 {
        label = "led0";
        gpios = <&pio  0  17  GPIO_ACTIVE_HIGH>;
    };

    superled1 {
        label = "led1";
        gpios = <&r_pio  0  10  GPIO_ACTIVE_HIGH>;
    };
};  
重編裝置樹並更新後啟動系統, 然後就可以檢視到myleds裝置節點產生platform_device資訊:
^_^ / # ls /sys/bus/platform/devices/myleds/
driver/          leds/            of_node/         subsystem/
driver_override  modalias         power/           uevent

//看到有driver目錄即表示已與platform_driver匹配上了.

^_^ / # ls /sys/bus/platform/devices/myleds/leds/led
led0/  led1/    
//表示在led子系統裡會創建出/sys/class/leds/led0   /sys/class/leds/led1目錄

控制led:
echo 1 > /sys/class/leds/led0/brightness   // label為led0的led燈亮
echo 0 > /sys/class/leds/led0/brightness   // label為led0的led燈滅

如無法通過程式碼分析出裝置樹裡應怎樣描述裝置,可以參考核心原始碼裡的:Documentation/devicetree/bindings/leds/leds-gpio.txt