2007年8月28日 星期二

Linux SPI driver Part 1

[目的]:
如何在Linux下,使用Kernel support的SPI driver

[註記]:
紀錄Porting SPI driver從開始到結束,因為目前還未成功,就先紀錄一下,等到完成時在修補不對的地方

[環境]:
硬體:使用SOC上的四根GPIO(不需要特定的SPI hardware)
軟體:最終是要porting在Linux 2.6.18.x,但2.6.22.x才開始有spidev(SPI device讓user problem 可使用open() read() write() ioctl() close() API有限制性的half-duplex去存取SPI device) 所以過程中需要把一些2.6.22.x跟spidev相關的程式也搬到2.6.18.x下

[第一部:Study]:

1. ~/Documentation/spi/spi-summary

- 如果沒有特定SPI controller存在,GPIO可以用來建立一個low speed "bitbanging" adapter

- 包含了kerneldoc和一些source code請務必要看

- SPI request是I/O quenes的方式FIFO的順序依序執行,基本上使用asynchronously機制,等執行完畢呼叫相對應的callback function,但也有簡單的wrapper將它包裝成synchronously,例如最常見包裝成"wirte a command and read its response"

-有兩類別的SPI driver
Controller drivers:會直接接觸到Hardware,本篇會使用的為四根一般用的GPIO,也就是bitbang方式
Protocol drivers:pass messages,透過Controller driver去做SPI的動作

-struct spi_device:在Controller driver & Protocol driver二者之間的master-side interface

-架構中會有幾個node
/sys/class/spi_master/spiB:class device for controller managing bus "B",因為在bus B上可以共用SCLK, MOSI, MISO (除了每顆slave需要自己的CS)
/sys/bus/spi/devices/spiB.C:symlink, spi_device for on bus "B" & chipselect "C"
/sys/bus/spi/drivers/D:driver for one or more spi*.* devices

-How does board-specific init code declare SPI devices?

DECLARE CONTROLLERS
首先要宣告SPI controllers exist,在SOC based board下通常為platform devices,然後需要一些platform_data來讓他操作正常,這邊就是要宣告有個GPIO方式
最後會呼叫到這個register_platform_device()

DECLARE SLAVE DEVICES
列出存在在版子上的SPI slave devices
static struct spi_board_info spi_board_info[] __initdata = {
{
.modalias = "CHIP",
.platform_data = &CHIP_info,
.mode = SPI_MODE_3,
},
};
最後會呼叫到這個spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info))

-How do I write an "SPI Protocol Driver"
static struct spi_driver CHIP_driver = {
.driver = {
.name = "CHIP",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = CHIP_probe
.remove = __devexit_p(CHIP_remove),
.suspend = CHIP_suspend,
.resume = CHIP_resume,
};
這driver core會自動試著去bind this driver到任何device 當board_info中modalias為"CHIP"

static int __devinit CHIP_probe(struct spi_device *spi) {
struct CHIP *chip;
sturct CHIP_platform_data *pdata;
pdata = &spi->dev.platform_data;
chip = kzalloc(sizeof *chip, GFP_KERNEL);
dev_set_drvdata(&spi->dev, chip);

}

本篇會用到wrapper, spi_write_then_read() function, spi_w8r16()

-spi_device的下為driver,上為sysfs/input layer/ALSA/networking/MTD/character device or other Linux subsystems

-兩種memory使用方式
I/O buffer使用一般Linux rules, allocate them from heap or free page pool(類似local var)
spi_transfer static, 需要zero-init (類似global var.)

-How do I write an "SPI Master Controller Driver"
一個SPI controller會被registered到platform_bus,寫一個driver去bind這個device

spi_master使用spi_alloc_master()去allocate一個spi master
然後使用class_get_devdata()去拿到driver-private data allocated for that device

struct spi_master *master;
sturct CONTROLLER *c;
master = spi_alloc_master(dev, sizeof *c);
if( !master)
return -ENODEV;
c = class_get_devdata(&master->cdev);

此driver會init spi_master的欄位 包括bus number通常等於platform device ID以及跟SPI core和SPI protocol driver互動的方法
完成init, spi_register_master去放到system上

2007年8月14日 星期二

過度換氣症候群

「過度換氣症候群」一般是指病因不明所導致的過度換氣現象
過度換氣會導致血中二氧化碳降低,酸鹼值因而升高,醫學上稱為「呼吸性鹼中毒」,並引起全身性的反應與症狀,其中較常見的包括血管收縮及血鈣降低

「慢性過度換氣症」的診斷必須排除生理性的疾病,因此肺功能、心電圖、胸部X光及抽血等基本檢查是必要的,以免延誤病人潛在的疾病。確定診斷可以動脈血液氣體分析,查驗出病人確實有二氧化碳過低的現象。
所以要詳問病史 :心臟疾病(心肌梗塞 心絞痛) 肺臟疾病(氣喘 肺氣腫 肺栓塞)
用藥史:剛剛有吃甚麼藥 (藥物中毒...)
剛剛發生什麼事:吵架 緊張 考試 比賽 密閉空間 壓力

治療首先要讓病人瞭解症狀發生的機轉,讓病人練習腹式呼吸,並做一些放鬆及減低壓力的治療



請參考消防署的這篇

有驚無險的喘息-「過度換氣症候群」


http://enews.nfa.gov.tw/issue/931223/images/health.htm

2007年8月10日 星期五

如何在compiler程式時檢查GCC版本?利用gcc的preprocessor

有些程式想要保證要被某版本以上gcc才能被編譯
寫在程式中 不是寫在configure檔

利用gcc前置處理器preprocessor
看參數 __GNUC__ , __GNUC_MINOR__ , GNU_PATCHLEVEL__

範例如下
hello.c
================================================
#include

int main(int argc, char *argv[]) {

// Check GCC version (>=2.9.x)
#if ( __GNUC__ >= 3 ) \
|| ( __GNUC__ >= 2 && __GNUC_MINOR__ >= 9 )
#warning "Your GCC is good( need >=2.9.x )"
#else
#error "Your GCC Version is wrong( need >=2.9 )"
#endif

//show GCC version
printf("You are using GCC:%d.%d.%d\n", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);

return 0;
}

================================================

gcc -o hello hello.c

2007年8月1日 星期三

ARM Programming model

Programming Model

Outline
1. Data operand size(word, halfword, byte...)
2. Endianness(little, big)
3. Processor mode(supervisor, user, FIQ, IRQ, abort, undefined, system)
4. Register(在不同的Processor的mode下 擁有不同bank的register set)
5. Program status register
6. Exceptions
7. ARM Instruction Set(Conditional execution...)

===================================================
1. Data operand size

Word: 32bits
nature operand size in a machine, ARM為32bit的處理器
(Hint: 因為每個處理器的nature size都不一定 所以使用typedef, 增加未來的移植性)

Halfword: 16bits
Byte: 8 bits

Load/Store指令可以使用byte, halfword, word 三種size
在Load時候可以使用zero-extending or sign-extending(P.S. store指令沒有必要extending)

===================================================
2. Endianness

ARM support little/big endian

Why這麼麻煩兩種都support?
IBM使用Big Endian且當初網路封包的spec很多是IBM制訂
Intel使用little Endian
所以此顆ARM最後是要用來當網通設備,選擇Big Endian在先天就會贏別人
但一般應用可以使用little endian(跟intel同樣架構的程式就會比較好porting過來)


little endian

big endian
ARM指令中並沒有直接可以選擇Endianness
===================================================
3. Processor mode

分成User, FIQ, IRQ, Supervisor, Abort, Undefined, System七種

User:
Normal program execute mode

FIQ:
Support a High-speed data transfer or channel process
快速的原因 更多個 banked register(R8~R12)可以直接使用.....

IRQ:
Use for general-purpose interrupt handling

Supervisor:
A protected mode for the OS

Abort:
data abort or prefetch abort後會進來, implement virtual memory and memory protection

Undefined:
Support software emulation of hardware coprocessors
例如沒有support VFD的CPU可以把軟體emulate寫在這邊來處理 或是 外部接一個coprocess指令

System:
可以執行privileged指令

Why要分這麼多種?
1. Support modern OS(User Space V.S. Kernel Space)
2. Control privileged instruction(Coprocessor instruction, memory access, MMU/Cache control, I/O control)
3. 指出目前CPU在特定的狀態下(Interrupt發生, unknown instruction, memory access abort...)
Reset時default mode?
Supervisor mode

How to change mode
software control
external interrupt or exception(FIQ, IRQ, Supervisor, Abort, Undefined)

P.S.
除了user mode其他都是privileged mode(就是所有recource都可以使用)

===================================================
4. Register

1. system mode跟user mode 用的register bank其實是一樣的, 只是system mode是privileged Mode
2. 其他mode R13,R14 & SPSR都是各個mode都有自己一組(不同bank register)
3. FIQ和多了R8~R12是進入FIQ mode自己特有的


===================================================
5. Program status register

NZCV Q J IF T mode

N: Negative
Z: Zero
C: Carried out
V: Overflow

Q: Sticky Overflow flag ????

J: Processor in Jazelle state

I: Disable IRQ
F: Disable FIQ

T: 0->ARM mode 1->Thumb mode

mode:
USER, FIQ, IRQ, SVC, ABORT, UNDEF, SYSTEM

===================================================
6. Exceptions

內部的trap (system call,SWI) 或是外部的Interrupt都算是exception

基本發生exeception會發生以下事情(這些事情以軟體的角度上是atomic)
1. R14 = return link //save return address
2. SPSR = CPSR //save CPSR
3. CPSR[4:0] = exception mode number
4. CPSR[5] = 0 //set ARM state
5. if == reset or FIQ then CPSR[6] =1 //當reset or FIQ才會把FIQ disable
6. CPSR[7] = 1 //disable IRQ
7. PC = exception vector address //branch to vector address

所以一般Enter/Exit軟體要做的是
Enter:
先把R14給存起來(調整真正回去位置, 也許這個指令要重新執行-4 or -8)
save 一些會用的register
ex:
SUB R14, R14, #4
STMFD SP!, {R1-R4, R12, R14}

Exit:
把這些pop到相對應的位置
ex:
LDMFD SP!, {R1-R4, R12, PC}^
^: the SPSR is copied into the CPSR. This is for returning from exception handles.
這個只能用到返回exception handler

(1)Reset (Priority: 1 Highest)
進入supervisor mode

Enter:
R14_svc = unpredictable value(反正reset後並沒有要返回哪個位置, 因為他就是reset啊)
SPSR_svc = unpredictable value(反正也不會在利用之前的PSR值)
CPSR[4:0] = 0b10011 //Supervisor mode
CPSR[5] = 0 //set ARM state
CPSR[6] = 1 //Disable fast interrupt
CPSR[7] = 1 //Disable IRQ
PC = 0x00000000 or 0xFFFF0000

(2)Data Abort (Priority: 2)

為什麼Priority這麼高? 因為不快點處理Data Abort所有的pipeline都會卡住

Enter:
R14_abt = address of the abort instruction + 8 //當初ARM設計為三層pipeline, 而發生data abort是在第三state所以PC值已經前進+8
SPSR_abt = CPSR
CPSR[4:0] = 0b10111 //Abort mode
CPSR[5] = 0 //set ARM state
CPSR[7] = 1 //Disable IRQ
PC = 0x00000010 or 0xFFFF0010
Exit:
SUBS PC, R14, #8

(3)FIQ (Priority: 3)
Enter:
R14_fiq = address of the next instruction to be executed + 4
SPSR_fiq = CPSR
CPSR[4:0] = 0b10000 //FIQ mode
CPSR[5] = 0 //set ARM state
CPSR[6] = 1 //Disable fast interrupt
CPSR[7] = 1 //Disable IRQ
PC = 0x0000001C or 0xFFFF001C
Exit:
SUBS PC,R14,#4


(4)IRQ (Priority: 4)
Enter:
R14_irq = address of the next instruction to be executed + 4
SPSR_irq = CPSR
CPSR[4:0] = 0b10010 //IRQ mode
CPSR[5] = 0 //set ARM state
CPSR[7] = 1 //Disable IRQ
PC = 0x00000018 or 0xFFFF0018
Exit:
SUBS PC,R14,#4


(5)Prefetch Abort (Priority: 5)
Enter:
R14_abt = address of the abort instruction + 4
SPSR_abt = CPSR
CPSR[4:0] = 0b10111 //Abort mode
CPSR[5] = 0 //set ARM state
CPSR[7] = 1 //Disable IRQ
PC = 0x0000000C or 0xFFFF000C
Exit:
SUBS PC,R14,#4

(6)Undefined instruction (Priority: 6)
Enter:
R14_und = address of next instruction after the undefined instruction//這個指令做完也不需要重作 就直接做一個指令即可
SPSR_und = CPSR
CPSR[4:0] = 0b11011 //Undefined mode
CPSR[5] = 0 //set ARM state
CPSR[7] = 1 //Disable IRQ
PC = 0x00000004 or 0xFFFF0004
Exit:
MOVS PC, R14

(7)SWI (Priority: 6)
Enter:
R14_svc = address of next instruction after the SWI instruction//這個指令做完也不需要重作 就直接做一個指令即可
SPSR_svc = CPSR
CPSR[4:0] = 0b10011 //SWI mode
CPSR[5] = 0 //set ARM state
CPSR[7] = 1 //Disable IRQ
PC = 0x00000008 or 0xFFFF0008
Exit:
MOVS PC, R14



===================================================
7. ARM Instruction Set

ARM state: 32 bit fix length(需要aligned)
Thumb state: 16 bit fix length
Load/Store架構(跟memory要東西只能靠Load/Store指令)

分成branch, Data-processing, Load/Store, Status register transfer, Coprocessor, Exception-generating 這幾大類

Conditional Execution
Add an S suffix to an ARM data processing instruction to make it update the ALU status flags in the CPSR
ex:
ADDS r0, r1, r2

Do not use the S suffix with CMP, CMN, TST, TEQ. These comparison instructions always update the flags

最大公因數
while( r1 != r2 ) {
if( r1 > r2 )
r1 = r1 - r2;
else
r2 = r2 - r1;
}

gcd:
CMP r1, r2
SUBGT r1, r1, r2
SUBLT r2, r2, r1
BNE gcd