顯示具有 Linux 驅動程式 標籤的文章。 顯示所有文章
顯示具有 Linux 驅動程式 標籤的文章。 顯示所有文章

2024年3月25日 星期一

[Linux Driver] 技術回覆整理

 

Q0: Driver 開發方式為何?


Ans: 

請參考"Linux Driver開發的模式"  https://blog.ittraining.com.tw/2024/02/linux.html

 

Q1: 在Linux 驅動程式-> Linux GPIO host control 的bcm2708_gpio.c 是在哪個路徑下?


Ans: 課程使用的是kernel 4.4.x , 其關於bcm2708_gpio.c 改為 driver_gpio.c
路徑位於 /home/pi/linux-raspberrypi-kernel_1.20190401-1/drivers/bcma



Q2: 為何在範例"LinuxDriver_Data_20191001 \Code\14-GPIO-RPI_H\gpio.c"裡

只有定義

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ittraining.com.tw");
MODULE_DESCRIPTION("A Simple GPIO Device Driver module for RaspPi");

而沒有定義
module_init();
module_exit();

Ans:

原始module 進入及離開點, 都是固定這個名字,
int init_module(void)
void cleanup_module(void)

若想要換別的名字,可以用以下巨集代換成別的名字.
module_init(led_init_module);
module_exit(led_remove_module);

Q3: 02_BTN_LED 裡的local_irq_save(flags), 禁止的irq是button_irq, 對嗎?

Ans:  不是. 

local_irq_save(flags)   //是對本次被中斷的CPU, 禁止它再接受何任何中斷

/*the critical section of code*/
.
local_irq_restore(flags) //還原被中斷前的CPU狀態


Q4:  Raspberry Pi 開發板接上電源後(I/O板接電腦USB埠, 主板接USB電源), 開機訊息或是下dmesg會常出現以下:

[ 10.391505] Under-voltage detected! (0x00050005)
[ 14.561447] Voltage normalised (0x00000000)

Ans:  其實可以略過此訊息, 但如果要消除此訊息.. 另外購買 2.5A power supply 供電, 不能用PC的 USB供電,因為電腦電流供應不足.

Q5:   關於中斷控制器

   這個local arm周邊的 具體是甚麼樣的周邊 請舉例
   這些周邊也接到GPU的中段控制器裡嗎? 
   另外, 中斷控制器有沒有在GPU裡?
   bcm2835 GPU ARM內部的IP簡圖去哪查?

Ans: 


(1) 來自ARM內部本身的中斷, 主要就是ARM 內部timer和作為和GPU溝通界面(Mailbox)的中斷通知
(2) 不知道,但也不用管GPU內部的中斷控制器
(3) 沒有, 我們講的中斷控制器是用來通知ARM 而己
(4)   Hardware architecture:   GPU ----> Interrupt controller ---> ARM


   資料來源: BCM2835-ARM-Peripherals  Ch7 Interrupts

 

Q5: 如何自己編譯核心或準備本機開發模式

請參閱 "Quick guide for building Raspberry Pi kernel 4.4.x"


Q6:

暫存器的確有可能因為中斷而改變狀態,但不懂得是明明已經說是存取暫存器
並不是一般放在記憶體的變數, 根本沒所謂保存在記憶體裡阿




上面這段描述是不是錯的?  我看gpio dirver也沒有加所以volatile?






Ans: 

(1) 現在計算機架構採用Memory-mapped I/O ,  將Peripheral Register  (Special Function Register)也映射在記憶體的一個區段, 所以存取週邊亦等同於存取記憶體.
   
(2)也許  readl 函式宣告像這樣.  readl (volatile uint32_t* paddr)

Q6 :電壓準位的問題

  Ans: 



2024年2月2日 星期五

Linux Driver開發的2種模式


(1)本機開發模式:

 人在本機上直接開發Driver, 本機可以是PC或Raspberry pi 這類的機器。當然你要能進入到本機裡開發,Toolchain (就是程式繑譯的相關工具),這個工具必須配合本機的作業系統環境安裝。編譯程式所編譯出來的code 是可以直接在本器上執行的。

P.S. 艾鍗的Linux Driver 課程是刻意準備成"本機開發模式"。為了給學員把學習重點放在"Linux Device Driver "上學習,而不要花太多時間在其他嵌式入開發的支微末節。這就好像學習"Window Device Driver"一樣,先專注Windows 作業系統驅動程式的軟體架構和流程。







(2)嵌入式開發模式:


通常本機是PC,而Target端是MCU/SoC開發板,當然也可以是Raspbeery Pi。 把Pi當作Target 端. 人在本機上寫Driver,但也得先安裝Toolchain ,這個工具也必須配合本機的作業系統環境安裝,但這個Toolchain 的Compiler 稱作Cross-compiler, 因為它產出的二進位檔是給Target 端執行的。本機多半和Target 端的CPU不同,是無法讀懂此二進位的指令格式,此二進位檔必會有一個方式傳到Target端去使用。



[Linux Driver] 技術回覆整理

 

2022年6月25日 星期六

UART over TTL/RS232

 

UART 是軟體層面的 serial 通訊協議(也可以借力 UART IC來完成)。定義baudrate, start bit, stop bit , data bit, parity check 等等, 但這些0和1都是只邏輯的0和1。而實際走的硬體訊號, 則要看使用的是TTL還是 RS232。TTL和 RS232 描述的是 Physical 電氣訊號。

TTL (transistor-transistor logic) 就是定義 0V 為 0,Vcc 為 1 , 一般 MCU、SoC 都會用的方式。  RS-232 定義 -3 to -25V 為 0, 而+3V~+25V為1。不過 RS232 是一個完善的標準通訊規範,所以尚包含硬體流量控制的機制及 Connector 的型式(如DB-9) 等內容。正常的PC 用的就是RS232 界面。不過若僅僅只是做基本的傳輸,不是拿來接數據機(Modem)這類的,其實也只是用了其中3條通訊線而已,TXD, RXD, GND。現在你懂了,為何 UART over TTL 只有3條訊號線的原因。

Source image: https://components101.com/


PC(RS232)—----- UART ——MCU (5V), 因為電壓不同所以要做level shifter , 這可以使用MX232之類的IC來完成. UART over RS-232 (上) 及 UART over TTL (下) 傳送 01010101的電壓圖。總之,我送0/1, 你要解讀成0/1, 而不是解讀成1/0。
Source Image: https://www.sparkfun.com/

相關文章: 


相關課程: 
[1] Linux 驅動程式設計 -實作LINUX UART/TTY Driver
[2] ARM Boot loader 設計-實作UART
[3] MUC 韌體設計-UART驅動


2020年10月2日 星期五

Quick guide for building Raspberry Pi kernel 4.4.x


For  Driver course (www.ittraining.com.tw)

1) 安裝toolchain (Cross compiler)

# For 32 位元Linux 

export PATH=/home/student/pi/tools/arm-bcm2708/arm-bcm2708-linux-gnueabi/bin:$PATH

# For 64 位元Linux
export PATH=/home/student/pi/tools/arm-bcm2708/arm-bcm2708-linux-gnueabi-x64/bin:$PATH


root@ubuntu:/home/student/pi# arm-linux-gnueabihf-gcc -v

..
gcc version 4.8.3 20140303 (prerelease) (crosstool-NG linaro-1.13.1+bzr2650 - Linaro GCC 2014.03)





2) Linux kernel 編譯

#進入kernel source tree

cd linux  

# 產生 .config


For Raspberry Pi 2, Pi 3, Pi 3+, and Compute Module 3 default build configuration

KERNEL=kernel7
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcm2709_defconfig

For Raspberry Pi 4
KERNEL=kernel7l
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcm2711_defconfig

#編譯 Linux kernel、kernel module 及 device tree

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage modules dtbs -j 2
# 安裝  kernel module

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=../modules modules_install



#複製kernel image & dtb 到 Raspberry Pi


sudo cp arch/arm/boot/zImage /boot/$KERNEL.img
sudo cp arch/arm/boot/dts/*.dtb /boot/
sudo cp arch/arm/boot/dts/overlays/*.dtb* /boot/overlays/


#複製 kernel modules 到 Raspberry Pi


tar zcvf modules.tar.gz  modules

scp modules.tar.gz root@192.168.1.141:/root

tar zxf modules.tar.gz
解開後將 <kernel version> 目錄放到 /lib/modules/


----------------------------------------------------
在 Raspberry Pi 編譯kernel module

1.) copy kernel source to  Raspberry Pi

scp linux-rpi-4.14.98-v7.tar.gz root@192.168.1.157:/root

tar zxf linux-rpi-4.14.98-v7.tar.gz

2.)建立連結到 kernel source

cd /lib/modules/4.14.98-v7
ln -sf  /root/linux-rpi-4.14.98-v7 build




------------------------------------------------------------------------------------
3.) build  driver module from source

cd kernel_driver/01_LED 
.
├── chr_led.c
├── Makefile
└── test.c

make 


├── chr_led.c
├── chr_led.ko
├── chr_led.mod.c
├── chr_led.mod.o
├── chr_led.o
├── Makefile
├── modules.order
├── Module.symvers
└── test.c

發生 fixdep error! Exec format error

進入kernel source 重新產生ARM版本的工具集

cd /root/linux-rpi-4.14.98-v7
make scripts 







4.)   測試 module

#載入模組sudo insmod chr_led.ko

#顯示所有模組
lsmod




#用user應用程式測試driver
gcc test.c -o test

sudo ./test   ==> 你會看見LED 在閃礫

#卸載模組
rmmod chr_led







2019年12月16日 星期一

什麼是Device Tree?



什麼是Device Tree?

目前Raspberry Pi 3 的kernel 核心為 4.4.y, 只可以使用 Device Tree 來描述 Internal SoC 及 External (Board level) 的硬體資源.

Device Tree 就是把 Platform Device 架構中用來描述硬體資源的部份給抽出來, 因為Platform Device 的檔案是用C code去描述的, 但概念上硬體資源的描述應和程式無關也與Linux版本無關. 用C 去描述很慘, 因為只要重新定義硬體資源都要重新編譯核心, 很浪費時間. 再來, 要去描述硬體資源,如果不熟悉C的語法, 也很難去寫 ,因為C語法不對會導致編譯失敗。

 Device Tree架構, 解決了上述這些問題, 不用懂C也能定義硬體資源, 如此硬體工程師也可以做了,因為pin 腳拉那根做什麼功能, 硬體工程師最清楚 ,不然做軟體,寫code的人就要自己去對線路圖 (也不是不行, 但如果有對的人做掉, 豈不更快!)


硬體資源: 包含 SoC-level (.dtsi)及 board level (.dts)

  1. 描述CPU, RAM size
  2. 描述 Memory-mapped 上的元件, PWM、UART、I2C、SPI等晶片內部元件的描述, 如暫存器位址及大小, 中斷號碼、DMA,, pinctrl
  3. 描述外部連接裝置, 如裝置規格, 以flash來說,包含 page size, flash size等資訊, 通常寫device driver會從這裡取得裝置資訊. 外部裝置通常使用 device tree overlay (產生.dtbo)的方式而不要直接修改.dts.
Device Tree  
1.  不用重新 compile source code 即可以更改系統的 configuration
2.  當硬體只有小小的更改,只需要 dts 檔小改, 然後再重新編譯出dtb即可。
3.  可以重複使用已存在的 dts 檔在其他地方,也可以覆蓋過去定義的功能
4.  移除 C code ,使得硬體的描述架構更清楚。 




實測:

(1) 加入MCP3008 ADC device到 device tree 中, 會自動掛載 .compatible="mcp3008" 的驅動程式. 

> dtc -@ -I dts -O dtb -o mcp300x.dtbo mcp300x.dts
sudo dtoverlay ./mcp300x.dtbo

(2) 讀取ADC channel 0

cat /sys/bus/iio/devices/iio:device0/in_voltage0_raw





References










2019年8月14日 星期三

[Linux Driver 問題] Linux GPIO Host Controller驅動程式



目前課程影片進行到CH3-3 Linux GPIO Host Controller驅動程式,有一些問題如下:

  1. 01_LED中chr_led.c使用gpio_set_value對GPIO Pin設定輸出值,往Kernel Source Code追: gpio_set_value -> __gpio_set_value -> gpiod_set_raw_value -> _gpiod_set_raw_value ->


    static void _gpiod_set_raw_value(struct gpio_desc *desc, bool value)
    {
            struct gpio_chip        *chip;

            chip = desc->chip;
            trace_gpio_value(desc_to_gpio(desc), 0, value);
            if (test_bit(FLAG_OPEN_DRAIN, &desc->flags))
                    _gpio_set_open_drain_value(desc, value);
            else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags))
                    _gpio_set_open_source_value(desc, value);
            else
                    chip->set(chip, gpio_chip_hwgpio(desc), value);
    }

    追到最後是課程影片中提到的struct gpio_chip,但是課程提供的Image中找不到bcm2708_gpio.c這個檔案,struct gpio_chip中的function應該都是實作在這個檔案中?
        ~/linux-rpi-4.4.y/drivers/bcma/driver_gpio.c  ==> gpio chip driver 
  1. 根據課程影片,gpio_set_value是上層API,由Kernel定義。struct gpio_chip中的function pointer實作則為底層硬體相關,這個部分理論上應該是SoC廠實作並提供給客戶嗎?想知道業界實務上怎麼分配?實務上,如果拿到一塊公版,SoC Vendor會提供整包Linux或Android Source Code,我們只需要針對自己選擇的外接硬體寫驅動程式嗎?
       
            
  1. SoC Vendor是否連Driver Template都會一起提供於整包OS Source Code中,只需要修改對應硬體部分?譬如,選擇的Display不同,選擇的Sensor不同,只需要在module_init()中修改初始化代碼?

             晶片廠通常提供的BSP ,會做好大部份SoC Driver. 若是自己自行外接的, 則要靠自己去撰寫或移植

             同類型硬體其Driver 寫法差異不大,所以可以參考現有的code去修改即可.. 

2019年8月12日 星期一

如何界定User Space Driver和Kernel Space Driver?


課程提供的Image中有一個bcm2835_clibbcm2835_clib資料夾,裡面有兩個資料夾。其中一個example應該是控制IT Shield上面不同External Device的Source Code。另一個src資料夾是不是類似"SoC HAL或BSP"之類的腳色,看起來比較像HAL,因為沒有包成板上Device控制的API,而是SoC Peripheral? 這是底層的HAL code 供上層使用 其中,下面的程式碼中為什麼是MAP_FAILED,而不是BCM2835_GPIO_BASEADDR之類的暫存器位置,而且找不到MAP_FAILED這個MACRO的定義 bcm2835_init () 會給正確值 另外,這樣子的寫法如果理解沒有錯誤,應該是Base Metal的寫法。為什麼blink.c編譯出來的執行檔可以直接執行? 這樣是不是等於不是走syscall -> LDD的方式,這樣Kernel怎麼管理這種Process? 這種方式如何界定User Space和Kernel Space? 這是User Space Driver 的寫法.. Kernel 只當它是一般User Process來管理..所以無法像Kernel Space Driver可以在底層做設備管理
User Space Driver 作法可以走 device file 的方式, 也可以用mmap(2)方式來操作SFR (special function register) . 細節可以自行參考 bcm2835_init () 函式

2016年6月18日 星期六

關於 74HC595 Shift Register


  74HC595 為一個位移暫存器: 8bit serial in, parallel out。主要可以透過控制 SER、RCLK, SRCLK 3個輸入接腳控制8個輸出(QA~QH), 這如同是一個3-to-8的Decoder。
  74HC595 位移暫存器可以作為I/O Expansion, 用3根GPIO獲得到8個輸出


  • Vcc
    Up to 6V (needs to be the same voltage as your microcontroller) – Usually 3.3 / 5v
  • QA to QH
    Shift Register Outputs.==> Register 輸出pin
  • SER.
    (Serial) Input for the next pin that gets shifted in.==> Register 輸入pin
  • SRCLK
    (Serial Clock) pulled high, 時 SER pin 狀態會丟入Register
  • RCLK
    (Register Clock) Needs to be pulled high to set the output to the new shift register values,  pulled high, 時 Register 內部8bit值會輸出到QA~QH. This must be pulled high directly after SRCLK has gone LOW again.
  • /SRCLR
    (Serial Clear) Will empty the whole Shift Register if pulled LOW, must be pulled High to enable.清除8bit register內容
  • /OE
    (Output Enable) This pin enables the output when tied to GND, & disabled when HIGH.=>控制輸出與否
  • OH' (pin 9) : You know how the last register just dumps its value when it is shifted over? 每次Shift Out 的值 (即bit 9的內容)

2015年9月27日 星期日

MX25L4005A SPI NOR Flash的使用方式



Raspberry Pi I/O Shield 上的MX25L4005A  SPI NOR Flash的使用方式


相關規格如下:
Flash size: 4Mbit (512KB), 其內部記憶體的組織結構為
  1. 64K Block size (for Block Erase) 
  2. 4K Sector size (for Sector Erase) 
  3. 256 Byte Page size (for Page Program) 



工作電壓為 .7 to 3.6 volt for read, erase, and program 操作
SPI 工作模式為 : Mode 0 & Mode 1

操作的相關注意事項: 
1.Flash 要寫入page之前,要先Erase
2.執行Page Program動作,byte 寫入"0", 無法寫入"1", 要執行Erase指令才能 將byte 設回"1"
3.資料送出的順序為 MSB First

執行動作

SE, BE, CE, and WRSR 會改變Flash內容的Command, 都要先設定WEL bit=1 (in RDSR bit 1), 一旦操作完成後, 它會自動reset (將WEL bit設回0)

操作方法: 

CS# goes low
     sending WREN (0x6); // set WEL=1
CS# goes high

CS# goes low
     sending <CMD>+{Flash Address}+{Data}//see command table
CS# goes high

CS# goes low
  deadline = jiffies + MAX_READY_WAIT_JIFFIES;
   do {
           status=sending RDSR(0x5) ;
            if (!(status & WIP) ) return 0;   //判斷是否操作完成
              cond_resched();                   // 再排程
        } while (!time_after_eq(jiffies, deadline));
CS# goes high 


Flash Address 的定址方法

NOR Flash 定址方法和一般SRAM,ROM, EEPROM 的定址方址相同,容量有多大,位址線就有多少條。容量有512 Kbyte 表示存取某一位置為需要19條位址線來定址。一般Parallel NOR Flash 介面,就會在IC元件上看到位址線 A0~A18的pin腳。但若為使用較少Pin count 以縮小IC 大小, 使用serial  Flash 是另一種解決方案。MX25L4005A  即是採用serial 的 SPI 介面。在送出位址時得用到3個byte ,也才足以表示這19條位址訊號。

位址結構:

位址用以定址到某一個block下的某一個sector, 再到某一個sector下的某一個page, 再到該page 內的offset。

若要進行Flash Block Erase (BE) 時, 描述Flash Addr [18:16] 。
若要進行Flash Sector Erase (SE)時, 描述Flash Addr [18:12]。
若要進行Flash 讀取(READ)時, 描述Flash Addr [18:0]。
若要進行Flash 寫入(WRITE)時, 描述Flash Addr [18:0]。
(每次寫入都是基於某一個page #內寫入)




2015年9月7日 星期一

Linux中斷控制器的抽象封裝



BCM2835內部的中斷控制器:


BCM2835內部的中斷控制器的架構:  主要包含GPU本身中斷(64個中斷)及21個基本中斷, 其中在 21個基本中斷,除了ARM IRQ外也同時包含了部份GPU的中斷在內 , 例如IRQ 79是GPU的 IRQ 53 (此為I2C中斷觸發),IRQ 80是GPU的 IRQ 54 (此為SPI中斷觸發)。而BCM2835 其全部54根GPIO 都是有產生中斷能力的, 由GPIO controller 產生中斷進入到中斷控制器的IRQ是配置在IRQ 49~52。



GPU  Interrupt Pending Bit (0~63)

ARM Basic Interrupt Pending Bit (0~20)



關於BCM2835 GPIO控制器

 共有54根GPIO分成2個bank , 一個管0~31, 另一個管32~53。由GPIO所產生中斷訊號共有3條, gpio_int[0],gpio_int[1], gpio_int[3], 分別是中斷控制器IRQ49, IRQ50及IRQ52,gpio_intp[0]是表示GPIO 0~31有產生中斷,而gpio_intp[1]是表示GPIO為32~53有中斷產生,而gpio_intp[3]是表示任一根GPIO中斷產生,不論是bank 0或bank1。





GPIO Event Detect Status Register


GPIO若要要產生中斷能力, 則必須設定 GPRENn GPFENn GPHENn, GPLENn, 4個暫存器,
分別用來設定欲中斷觸發類型, Rising Edge Enable, .Falling Edge, Enable,High Level Enable, Low Level Enable。若某根GPIO要Rising 及Falling Edge 都要觸發中斷, 則須別在GPRENn 及GPFENn 對應的bit設1.
每當有中斷產生時, 其對應pending register 的bit都會為1, 用以標示該中斷已產生。每當ISR 跑完後,都必須將其bit 清除, 否則該中斷將無法再繼續產生。而通常清除的方法,就是在對應的bit 上"寫 1" 。在BCM2835的GPIO中斷其等同於pending register的意義是 EDS Register (Event Detect Status ) 用以記錄GPIO 的中斷觸發事件 (不論是Edge 或 Level 都記錄)。


Linux中斷通用層的資料結構

Linux 必須有能力處理不同Interrupt controller 硬體間的差異,如此一來才具備可攜性。Linux底下主要透過三個資料結構完成:

  1. struct irq_chip: 中斷硬體控制器,實作中斷控制晶片的行為,例如 mask IRQ, unmask IRQ
  2. struct irqaction: 指向中斷發生時動作行為, 即描述ISR
  3. struct irq_desc: 中斷描述表 Interrupt Descriptor Table (IRQ Line描述)

Linux中斷通用層的資料結構之關聯


建立GPIO Interrupt Descriptor Table

用一個for loop 產生多張Interrupt Descriptor (struct irq_dest), 每個Descriptor均有不同GPIO IRQ Line編號,但都指向同一個Interrupt controller Chip。

static void bcm2708_gpio_irq_init(struct bcm2708_gpio *ucb)
{
  unsigned irq;
  ucb->gc.to_irq = bcm2708_gpio_to_irq;

 //for loop 針對每一個 IRQ Line 建立 irq_desc 
  for (irq = GPIO_IRQ_START; irq < (GPIO_IRQ_START + GPIO_IRQS); irq++) 
 { 
  irq_set_chip_data(irq, ucb);  //set chip specific data for an irq (170+i)
  irq_set_chip (irq, &bcm2708_irqchip); //set the irq chip for an irq
  set_irq_flags (irq, IRQF_VALID);
  }

  setup_irq (IRQ_GPIO3, &bcm2708_gpio_irq);  // 稍後說明
}



註冊GPIO IRQ 使用 request_irq() API

每一個IRQ Line都有了irq descriptor 之後, 就可以開始註冊每一個GPIO ISR , 可直接透過來完成。

 request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)


request_irq的第一個參數irq, 即是向系統的IDT(Interrupt Descriptor Table), (IDT為struct irq_desc的集合) 找到對應的irq_desc, 然後登記ISR,即 irq_desc->action=handler。例如,
request_irq(193, .....); 從IDT尋找179 是屬於GPIO interrupt chip controller, 所以一旦request_irq成功, 則IRQ 193 是掛在名為GPIO irq_chip, 而非ARMCTRL irq_chip。

誰來執行我們的GPIO IRQ Handler

在一個程式代碼中, 最後一行呼叫   setup_irq (IRQ_GPIO3, &bcm2708_gpio_irq); 其目的是向Interrupt controller Chip(ARMCTRL) 註冊IRQ 52 及其ISR bcm2708_gpio_irq 。
bcm2708_gpio_irq為一個 struct irqaction 結構, 其中 .handler指向一個 interrupt handler "bcm2708_gpio_interrupt" ,由此handler 來執行我的IRQ Handler。

bcm2708_gpio_interrupt 函式作法就是掃描 GPIO EDS Register , 若bit 為1, 則執行 generic_handle_irq (gpio_to_irq(gpio));  目的是透過 irq 去搜尋其 irq_desc, 然後再由irq_desc->action 呼叫對應註冊的ISR。struct irqaction為一個串列, 所以會不斷呼叫下去,直到有一個ISR 傳回IRQ_HANDLED。當IRQ為Shard IRQ 時,則發生此情況,若此次非由該裝置產生,則傳回IRQ_NONE, 但一定要有一個ISR傳IRQ_HANDLED,否則將會導致kernel pacnic.



static struct irqaction bcm2708_gpio_irq = {
  .name = "BCM2708 GPIO catchall handler",
  .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
  .handler = bcm2708_gpio_interrupt,
};

static irqreturn_t bcm2708_gpio_interrupt (int irq, void *dev_id)
{
  unsigned long edsr;
  unsigned bank;
  int i;
  unsigned gpio;
  for (bank = 0; bank <= 1; bank++) { //scan pending register (Event Detect Status)
  edsr = readl(__io_address(GPIO_BASE) + GPIOEDS(bank));
  for_each_set_bit(i, &edsr, 32) {     // i1bit位置處
  gpio = i + bank * 32;
  generic_handle_irq (gpio_to_irq(gpio)); //call GPIO ISR
      // clear pending regisgter
  writel(1<<i,__io_address(GPIO_BASE) + GPIOEDS(bank));
  }
  }
  return IRQ_HANDLED;
}


定義一個GPIO 中斷的irq chip

static struct irq_chip bcm2708_irqchip = {
 .name = "GPIO",
 .irq_enable = bcm2708_gpio_irq_unmask,  
.irq_disable = bcm2708_gpio_irq_mask,  
.irq_unmask = bcm2708_gpio_irq_unmask, 
.irq_mask = bcm2708_gpio_irq_mask,
 .irq_set_type = bcm2708_gpio_irq_set_type
};


說明: 宣告一個 struct irq_chip, 名稱為"GPIO", 並設定其相關底層的控制設定 ,如底下GPIO mask的程式, 即在對應的pin上. 對GPRENn GPFENn GPHENn, GPLENnBCM2835 設0。(ARM Peripherals Manual GPIO p.89)

static void bcm2708_gpio_irq_mask(struct irq_data *d)
{
  unsigned irq = d->irq;
  struct bcm2708_gpio *gpio = irq_get_chip_data(irq);
  unsigned gn = irq_to_gpio(irq);
  unsigned gb = gn / 32;
  unsigned long rising  = readl(gpio->base + GPIOREN(gb));
  unsigned long falling = readl(gpio->base + GPIOFEN(gb));
  unsigned long high    = readl(gpio->base + GPIOHEN(gb));
  unsigned long low     = readl(gpio->base + GPIOLEN(gb));
  gn = gn % 32;
  writel(rising  & ~(1 << gn), gpio->base + GPIOREN(gb));
  writel(falling & ~(1 << gn), gpio->base + GPIOFEN(gb));
  writel(high    & ~(1 << gn), gpio->base + GPIOHEN(gb));
  writel(low     & ~(1 << gn), gpio->base + GPIOLEN(gb));
}


DEMO


實作出來的結果, GPIO 控制器所產生的中斷, 是註冊在中斷控制器(ARMCTRL)的IRQ 號碼52 (gpio_int[3]), IRQ Handler 的名稱為 "BCM2708 GPIO catchall handler", 此IRQ Handler 再來scan GPIO controller 是那一根gpio pin# 產生中斷? 接著再呼叫該gpio pin#所註冊的ISR,此ISR即為
request_irq(193, irq_handler ,"my_button_int",..) 所註冊的ISR, 此中斷193號碼是掛在GPIO controller 的中斷。GPIO pin# IRQ 號碼為 170+GPIO Pin number。 另外, 因為所有GPIO 產生的中斷都是串接在中斷控制器(ARMCTRL)的, 故IRQ 52 的CPU中斷次數為個別GPIO 中斷次數的加總。

cat /proc/interrupts 輸出內容