如何在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
-
- 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上
沒有留言:
張貼留言