顯示具有 driver 標籤的文章。 顯示所有文章
顯示具有 driver 標籤的文章。 顯示所有文章

2019年3月6日 星期三

Access memory-mapped registers in Linux kernel space.

Source https://stackoverflow.com/questions/16935041/how-to-write-register-from-linux-kernel-module-cpu-arm

void __iomem *io = ioremap(REGBASE, SZ_4K);
writel(0x00000002, io + 0x0004);

2019年2月6日 星期三

Linux中斷下半部tasklet

Source https://codertw.com/%E7%A8%8B%E5%BC%8F%E8%AA%9E%E8%A8%80/609807/


tasklet是Linux核心中“可延遲執行”機制、或“中斷下半部”的一種。tasklet的優先順序別較低,而且中斷處理過程中可以被打斷。但被打斷之後,還能進行自我恢復,斷點續執行。

1.資料結構

struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsigned long);
unsigned long data;
};

2.初始化

/* 靜態 */
//可以直接用tasklet_schedule()排程
#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
//需要使用tasklet_enable()使能,才可以被排程執行
#define DECLARE_TASKLET_DISABLED(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }
/* 動態 */
extern void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long data);

3.排程tasklet

//將tasklet加入到排程連結串列裡面,tasklet就能得到執行,每呼叫這個函式一次,tasklet只能執行一次,要再次執行需要重新呼叫這個函式
static inline void tasklet_schedule(struct tasklet_struct *t);

4.銷燬tasklet

//裝置關閉和模組解除安裝的時候,呼叫來殺死tasklet。如果當前tasklet在執行,會等待完成後,再殺死
extern void tasklet_kill(struct tasklet_struct *t);
extern void tasklet_kill_immediate(struct tasklet_struct *t, unsigned int cpu);

5.其他tasklet介面

static inline int tasklet_trylock(struct tasklet_struct *t)
static inline void tasklet_unlock(struct tasklet_struct *t)
static inline void tasklet_unlock_wait(struct tasklet_struct *t)
//比tasklet_schedule優先順序更高,可以得到更快處理
static inline void tasklet_hi_schedule(struct tasklet_struct *t)
//和tasklet_hi_schedule差不多,只是更安全
static inline void tasklet_hi_schedule_first(struct tasklet_struct *t)
//和tasklet_disable一樣,如果當前tasklet在執行,這個函式不會等待完成就先返回,當tasklet完成退出後,再禁止。 
static inline void tasklet_disable_nosync(struct tasklet_struct *t)
//禁止tasklet,即使tasklet_schedule已經把tasklet新增到排程連結串列裡,也得不到執行,必須要用tasklet_enable使能才可以。如果當前tasklet正在執行,tasklet_disable會等待執行完,然後禁止,返回。 
static inline void tasklet_disable(struct tasklet_struct *t)
//使能tasklet,和tasklet_disable要成對使用    
static inline void tasklet_enable(struct tasklet_struct *t)
//和tasklet_enable一樣
static inline void tasklet_hi_enable(struct tasklet_struct *t)
================================================================================================
struct tasklet_hrtimer {
struct hrtimer      timer;
struct tasklet_struct   tasklet;
enum hrtimer_restart    (*function)(struct hrtimer *);
};
extern void
tasklet_hrtimer_init(struct tasklet_hrtimer *ttimer,enum hrtimer_restart (*function)(struct hrtimer *),clockid_t which_clock, enum hrtimer_mode mode);
static inline 
int tasklet_hrtimer_start(struct tasklet_hrtimer *ttimer, ktime_t time,const enum hrtimer_mode mode)
static inline void tasklet_hrtimer_cancel(struct tasklet_hrtimer *ttimer)

6.驅動例項

#include 
#include 
#include 
#include 
static struct tasklet_struct test_tasklet;
static void function(unsigned long data)
{
printk(KERN_INFO "=======before schedule:%ld, in running:%ld=======\n",data,jiffies);
}
static int __init tasklet_test_init(void)
{
unsigned long tmp;
tmp = jiffies;
tasklet_init(&test_tasklet, function, tmp);
tasklet_schedule(&test_tasklet);
printk(KERN_ALERT "==============tasklet_test_init===============\n");
return 0;
}
static void __exit tasklet_test_exit(void)
{
printk(KERN_ALERT "==============tasklet_test_exit===============\n");
tasklet_kill(&test_tasklet);
}
module_init(tasklet_test_init);
module_exit(tasklet_test_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR( "wyy");

2018年8月21日 星期二

Linux interrupt

Q: Difference between enable_irq_wake and enable_irq
A: (not validated) https://stackoverflow.com/questions/26331348/difference-between-enable-irq-wake-and-enable-irq
If you check here, you'll see that enable_irq_wake invokes set_irq_wake_real that does not enable the irq.
Further more, take for example this driver: they enable/disable_irq the irq at open/close, while they enable/disable_irq_wake at suspend/resume.

Q: Difference between request_irq and request_threaded_irq
A:
extern int __must_check request_threaded_irq(unsigned int irq, irq_handler_t handler, irq_handler_t thread_fn, unsigned long flags, const char *name, void *dev); 

static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
{ return request_threaded_irq(irq, handler, NULL, flags, name, dev); }

handler : top half handler that can only (?) invoke limited APIs. Real-time response is expected.
thread_fn : bottom half handler that runs in kernel thread.

https://blog.csdn.net/qwaszx523/article/details/52635431
分析request_threaded_irq()函數中的各個形參
1>:irq:表示申請的中斷號。
2>:handler:表示中斷服務例程
3.> thread_fn:中斷線程化,此處傳遞的是NULL。NULL表示沒有中斷線程化。
    在Linux中,中斷具有最高的優先級。不論在任何時刻,只要產生中斷事件,內核將立即執行相應的中斷處理程序,等到所有掛起的中斷和軟中斷處理完畢後才能執行正常的任務,因此有可能造成實時任務得不到及時的處理。中斷線程化之後,中斷將作為內核線程運行而且被賦予不同的實時優先級,實時任務可以有比中斷線程更高的優先級。這樣,具有最高優先級的實時任務就能得到優先處理,即使在嚴重負載下仍有實時性保證。but,並不是所有的中斷都可以被線程化,比如時鐘中斷,主要用來維護系統時間以及定時器等,其中定時器是操作系統的脈搏,一旦被線程化,就有可能被掛起,這樣後果將不堪設想,所以不應當被線程化。 
4>.irqflags:表示中斷標誌位。
5>.devname:表示請求中斷的設備的名稱。
6>.dev_id: 對應於request_irq()函數中所傳遞的第五個參數,可取任意值,但必須唯一能夠代表發出中斷請求的設備,通常取描述該設備的結構體。共享中斷時所用。

http://webcache.googleusercontent.com/search?q=cache:MTmrEGHefQ8J:angledark0123.pixnet.net/blog/post/57650121-linux-request_threaded_irq%2528%2529-%25E8%2588%2587-request_irq%2528%2529-%25E5%25B7%25AE%25E7%2595%25B0+&cd=1&hl=zh-TW&ct=clnk&gl=tw

這幾年linux新增進來的function,讓irq handle能更簡潔快速,一些會sleep的動作可以在一開始request irq 的時候就指定好要由kernel thread來執行

也因為是在kernel context下執行,所以他不受interrupt context的atomic限制,可以有sleep的動作(像是i2c,spi和semaphore)
傳統的irq handle只能用worqueue或是tasklet等來處理bottom half的部份,top half 就考量到hw interrupt不能咬住太久所以要馬上放掉
這種新的threaded irq 能夠再hw irq上得到更快速的處理吧
然後interrupt process的pid 其實不存在,他顯示的是interrupt進來前被中斷的process的pid,所以你會看到interrupt process可能並沒有固定pid的原因就是這個
p.s 要確定你寫code的地方是不是atomic可以用might_sleep() 來確定,如果再的地方是atomic 也就是不能sleep和preempt的地方,在log裏面你會看到他會顯示isatomic &call stack
注意這個isatomic不會狂吐,他是有timer固定一段時間吐一下的


2015年9月27日 星期日

[Linux][Driver][I2C] 在device tree的架構下, i2c device與driver如何對應?


在device tree的架構下, i2c device 與 driver 是如何 match
原文: http://janbarry0914.blogspot.tw/2014/08/device-tree-i2c-device-driver-match.html

[文章重點]
了解在 device tree 的架構一下, i2c device 與 i2c driver 是如何 match 進而 call 到 driver 裡的 probe function (本文章是以 Qualcomm MSM8974 liquid board 為例)

[文章目錄]
  1. 在 device tree 架構下的作法
  2. device 如何被註冊
  3. driver 如何與 device 匹配

[在 device tree 架構下的作法]
Linux kernel driver 的架構都是由匯流排 ( bus ), 裝置 ( device ) 和 驅動程式 ( driver ) 所構成, 匯流排將裝置和驅動程式綁定. 當 Linux kernel 在註冊一個裝置的時候, 會尋找與之匹配的驅動程式; 相反的, 當 Linux  kernel 在註冊一個驅動程式的時候, 也會尋找與之匹配的裝置, 而匹配這動作是由匯流排負責完成.  然而 device tree 在做匹配綁定的概念跟之前的作法仍是一致的, 換句話說, 還是利用字串的比對, 只是由原本的 name 屬性改由 device tree 內的 compatible 屬性去做而已.


首先會說明裝置是如何被建立, 是如何被加到匯流排中, 接下來會說明驅動程式在註冊後, 匯流排如何去做這匹配的動作.

[device 如何被註冊]
那裝置是如何被加到匯流排中?  那就是透過 of_platform_populate( ) 這 function.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
 * of_platform_populate() - Populate platform_devices from device tree data
 * @root: parent of the first level to probe or NULL for the root of the tree
 * @matches: match table, NULL to use the default
 * @parent: parent to hook devices from, NULL for toplevel
 *
 * Similar to of_platform_bus_probe(), this function walks the device tree
 * and creates devices from nodes.  It differs in that it follows the modern
 * convention of requiring all device nodes to have a 'compatible' property,
 * and it is suitable for creating devices which are children of the root
 * node (of_platform_bus_probe will only create children of the root which
 * are selected by the @matches argument).
 *
 * New board support should be using this function instead of
 * of_platform_bus_probe().
 *
 * Returns 0 on success, < 0 on failure.
 */
int of_platform_populate(struct device_node *root,
   const struct of_device_id *matches,
   const struct of_dev_auxdata *lookup,
   struct device *parent)
{
 struct device_node *child;
 int rc = 0;

 root = root ? of_node_get(root) : of_find_node_by_path("/");
 if (!root)
  return -EINVAL;

 for_each_child_of_node(root, child) {
  rc = of_platform_bus_create(child, matches, lookup, parent, true);
  if (rc)
   break;
 }

 of_node_put(root);
 return rc;
}

從這 function 的註釋中了解到它會走遍整個 device tree 並為 每一個 device node 去創建 device. 接下透過 call flow來了解先後關係.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
DT_MACHINE_START(MSM8974_DT, "Qualcomm MSM 8974 (Flattened Device Tree)")
 .map_io = msm8974_map_io,
 .init_irq = msm_dt_init_irq,
 .init_machine = msm8974_init,
 .handle_irq = gic_handle_irq,
 .timer = &msm_dt_timer,
 .dt_compat = msm8974_dt_match,
 .reserve = msm_8974_reserve,
 .init_very_early = msm8974_init_very_early,
 .restart = msm_restart,
 .smp = &msm8974_smp_ops,
MACHINE_END

 msm8974_init( ) @ board-8974.c 
 |--> board_dt_populate( ) @ board-dt.c 
     |--> of_platform_populate( )  @ platform.c
        |--> of_plateform_bus_create( ) @ platform.c 
            |--> of_platform_device_create_pdata( ) @ platform.c 
               |--> of_device_alloc( ) @ platform.c
                   /*除了分配struct platform_device的記憶體,
                     還分配了該platform device需要的resource的記憶體*/  
               |--> of_device_add( ) @ device.c /*把這個platform device加入系統中*/  
                   |--> device_add( )  /* 它會call device_create_file( ) 
                                       在 sysfs中建立 attribute file for the device*/

[driver 如何與 device 匹配]
在看完 " device tree 如何新增 device 到系統中" 後, 接下來看 device driver 的註冊過程, 在此, 以 i2c device driver  - audio codec rt5627 為例.

 i2c_add_driver( ) @ i2c.h  
 |-> i2c_register_driver( ) @ i2c-core.c /*將 device_driver中的 bus_type 設成 i2c_bus_type */  
    |--> driver_register( ) @ driver.c  
        |--> bus_add_driver( ) @ bus.c /* 建立 sysfs file node 與 attr*/  
           |--> driver_attach( ) @ dd.c  
              |--> bus_for_each_dev( drv->bus, NULL, drv, __driver_attach) @ bus.c  
                  |--> __driver_attach @ dd.c  
                      |--> driver_match_device(drv, dev) @base.h  
                           |--> i2c_device_match( ) @ i2c-core.c  
                               /***********************************************  
                           如果是device tree, 會透過 of_driver_match_device( ) 去做匹配的動作  
                           如果不是device tree就改用 i2c_match_id( ) 去完成匹配的動作  
                              ***************************************************/  
                  |--> driver_probe_device( ) @ dd.c  
                       /*如果匹配成功, 接下來就要準備 call driver 中的 probe function*/
                       |--> really_probe( ) @ dd.c  
                           |--> i2c_device_probe( ) @ i2c-core.c  
                                |--> rt5627_i2c_probe( ) @ rt5627.c   

從上述的 call flow 得知, device tree 是透過 of_driver_match_device( ) 來做 device 與 driver 匹配的工作. 再從下述的 call flow 得知, 其實最後就是拿 device tree 中的 compatible 屬性跟 driver 中的 of_match_table->compatible 做字串的比較, 如果字串相同, 那就匹配成功囉.

 of_driver_match_device( ) @ of_device.h  
 |--> of_match_device(drv-> of_match_table, dev) @ device.c  
     |--> of_match_node( drv->of_match_table, dev->node) @ base.c  
         |--> of_device_is_compatible(node, drv->of_match_table->compatible) @ base.c 
             |--> of_compat_cmp( ) @ prom.h  
                  /*在 of_compat_cmp( ) 會透過 of_get_property()去取得
                    device tree 中 compatible 對應的值(字串), 
                    再跟 drv->of_match_table->compatible 所指的字串做字串比較*/
                 |--> strncmp( ) /*字串的比較*/  

我們再細看一下 of_match_node( ) 在做什麼事

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
 * of_match_node - Tell if an device_node has a matching of_match structure
 * @matches: array of of device match structures to search in
 * @node:  the of device structure to match against
 *
 * Low level utility function used by device matching.
 */
const struct of_device_id *of_match_node(const struct of_device_id *matches,
      const struct device_node *node)
{
 if (!matches)
  return NULL;

 while (matches->name[0] || matches->type[0] || matches->compatible[0]) {
  int match = 1;
  if (matches->name[0])
   match &= node->name
    && !strcmp(matches->name, node->name);
  if (matches->type[0])
   match &= node->type
    && !strcmp(matches->type, node->type);
  if (matches->compatible[0])
   match &= of_device_is_compatible(node,
      matches->compatible);
  if (match)
   return matches;
  matches++;
 }
 return NULL;
}
EXPORT_SYMBOL(of_match_node);

就 device tree 而言(大部分 driver 的 of_match_table 裡只有填寫 compatible 屬性, 例如: 在下述 rt5627_match_table  這例子裡, 就是只有填寫 compatible 屬性), 它會跑到 of_device_is_compatible( ) 去檢查是否匹配, 下面是就是 of_device_is_compatible( ) 完整的 source code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/** Checks if the given "compat" string matches one of the strings in
 * the device's "compatible" property
 */
int of_device_is_compatible(const struct device_node *device,
  const char *compat)
{
 const char* cp;
 int cplen, l;

 cp = of_get_property(device, "compatible", &cplen);
 if (cp == NULL)
  return 0;
 while (cplen > 0) {
  if (of_compat_cmp(cp, compat, strlen(compat)) == 0)
   return 1;
  l = strlen(cp) + 1;
  cp += l;
  cplen -= l;
 }

 return 0;
}
EXPORT_SYMBOL(of_device_is_compatible);

貼一下關於 of_match_table 是如何放在 driver 中 (以 audio codec rt5627為例)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#ifdef CONFIG_OF
static struct of_device_id rt5627_match_table[] = {
        { .compatible = "realtek,rt5627",},
        { },
};
#else
#define rt5627_match_table NULL
#endif

static struct i2c_driver rt5627_i2c_driver = {
 .driver = {
  .name = "rt5627",
  .owner = THIS_MODULE,
  .of_match_table = of_match_ptr(rt5627_match_table),
 },
 .probe =    rt5627_i2c_probe,
 .remove =   rt5627_i2c_remove,
 .id_table = rt5627_i2c_id,
};

看一下 device tree ( msm8974-liquid.dtsi ) 內, 我們新增一個 i2c device - rt5627 

 &soc {  
    ..  
     i2c@f9923000 {  
           realtek_rt5627@18 {  
                compatible = "realtek,rt5627";  
                reg = <0x18>;  
           };  
      };  
    ...  
 };  

我們將 Realtek rt5627 這顆 audio DAC ( 其 i2c address 是 0x18 ) 掛在 i2c bus 上 ( 其 physical address 為 0xf9923000 的 i2c bus 上).

[結論] 
在使用 device tree 時, 其匹配是利用 device tree 中 的 compatible 屬性, 跟 driver 中的 of_match_tabl e-> compatible 所指的字串做比較, 所以兩個字串要相同, 這樣才會匹配成功.

 [補充]
   針對 rt5627 這 i2c device 是如何被加進系統中做一個補充, 其實在device tree 章節中就有說明 device 是如何被加到系統中 ( board_dt_populate() ->  of_platform_populate( ) -> .... -> device_add( )), 但那只是針對一些父節點而言. 此例中, i2c@f9923000 是父節點, 而 realtek_rt5627@18 是子節點, 那子節點是何時被加到系統中? 在Qualcomm msm8974 平台上, 是在 i2c driver 的 probe function 中去實現, 所以是父節點做完 device <--> driver 的匹配動作之後, 在父節點的 driver 中的 probe function 裡再去找 device tree 中是否還有子節點, 如果有, 就開始一連串 device 註冊的動作.

 qup_i2c_probe( ) @ i2c-qup.c  
 |--> i2c_add_numbered_adapter( ) @ i2c-core.c   
     /* 宣告 i2c adapter 並給予 bus number*/  
 |--> of_i2c_register_device( ) @ of_i2c.c  
     /*走遍 child nodes, 並為 child nodes 註冊 device nodes*/  
     |--> i2c_new_device( ) @ i2c-core.c   
         /*為這 i2c device 依bus number 與 address命名, 例如: 4-0018  
          表示該 device 位於 bus 4, i2c address 是 0018*/  
        |--> device_register( ) @ core.c  
            |--> device_add ( ) @ core.c