2016年6月21日 星期二

spi study


DEVICE CREATION, DRIVER BINDING
===============================
The simplest way to arrange to use this driver is to just list it in the
spi_board_info for a device as the driver it should use:  the "modalias"
entry is "spidev", matching the name of the driver exposing this API.
Set up the other device characteristics (bits per word, SPI clocking,
chipselect polarity, etc) as usual, so you won't always need to override
them later.


When you do that, the sysfs node for the SPI device will include a child
device node with a "dev" attribute that will be understood by udev or mdev.
(Larger systems will have "udev".  Smaller ones may configure "mdev" into
busybox; it's less featureful, but often enough.)  For a SPI device with
chipselect C on bus B, you should see:

    /dev/spidevB.C ... character special device, major number 153 with
 a dynamically chosen minor device number.  This is the node
 that userspace programs will open, created by "udev" or "mdev".

    /sys/devices/.../spiB.C ... as usual, the SPI device node will
 be a child of its SPI master controller.

    /sys/class/spidev/spidevB.C ... created when the "spidev" driver
 binds to that device.  (Directory or symlink, based on whether
 or not you enabled the "deprecated sysfs files" Kconfig option.)


Do not try to manage the /dev character device special file nodes by hand.
That's error prone, and you'd need to pay careful attention to system
security issues; udev/mdev should already be configured securely.

If you unbind the "spidev" driver from that device, those two "spidev" nodes
(in sysfs and in /dev) should automatically be removed (respectively by the
kernel and by udev/mdev).  You can unbind by removing the "spidev" driver
module, which will affect all devices using this driver.  You can also unbind
by having kernel code remove the SPI device, probably by removing the driver
for its SPI controller (so its spi_master vanishes).

Since this is a standard Linux device driver -- even though it just happens
to expose a low level API to userspace -- it can be associated with any number
of devices at a time.  Just provide one spi_board_info record for each such
SPI device, and you'll get a /dev device node for each device.

BASIC CHARACTER DEVICE API
==========================
Normal open() and close() operations on /dev/spidevB.D files work as you
would expect.



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

The kernel device driver

After running "make" successfully, the folder "build-dir" is populated with the kernel source and the coding can commence.
Here's the driver source code. It's not exactly easy to understand and it probably could use some comments.

build_dir/linux-ramips_rt305x/linux-3.3.8/drivers/misc/spitest.c



  1. #include <linux/kernel.h>
  2. #include <linux/module.h>
  3. #include <linux/init.h>
  4. #include <linux/workqueue.h>
  5. #include <linux/spi/spi.h>
  6.  
  7. struct spitest {
  8. struct delayed_work work;
  9. struct device *dev;
  10. struct spi_device *spidev;
  11. unsigned char increment;
  12. unsigned char direction;
  13. };
  14.  
  15. struct workqueue_struct *workqueue;
  16.  
  17. static void spitest_loop( struct delayed_work *ptr )
  18. {
  19. struct spitest *item;
  20. item = (struct spitest *)ptr;
  21.  
  22. if (item->increment == 0x80)
  23. item->direction = 'd';
  24. else if (item->increment == 1)
  25. item->direction = 'u';
  26.  
  27. if (item->direction == 'u')
  28. item->increment <<=1;
  29. else if (item->direction == 'd')
  30. item->increment >>=1;
  31.  
  32. spi_write(item->spidev,&(item->increment),1);
  33.  
  34. queue_delayed_work(workqueue,ptr,10);
  35. }
  36.  
  37. static int __init spitest_probe(struct spi_device *dev)
  38. {
  39. int ret = 0;
  40. struct spitest *item;
  41.  
  42. printk("Spitest probe started\n");
  43.  
  44. item = kzalloc(sizeof(struct spitest), GFP_KERNEL);
  45. if (!item) {
  46. dev_err(&dev->dev,
  47. "%s: unable to kzalloc for spitest\n", __func__);
  48. ret = -ENOMEM;
  49. goto out;
  50. }
  51.  
  52. item->spidev = dev;
  53. item->dev = &dev->dev;
  54. item->direction ='u';
  55. item->increment = 1;
  56. dev_set_drvdata(&dev->dev, item);
  57. dev_info(&dev->dev, "spi registered, item=0x%p\n", (void *)item);
  58.  
  59. INIT_DELAYED_WORK(&(item->work),spitest_loop);
  60. queue_delayed_work(workqueue,&(item->work),10);
  61.  
  62. out:
  63. return ret;
  64. }
  65.  
  66. static int spitest_remove(struct spi_device *spi)
  67. {
  68. /*should be implemented but I'm lazy and this is just a test */
  69. return 0;
  70. }
  71.  
  72. static int spitest_suspend(struct spi_device *spi, pm_message_t state)
  73. {
  74. /*should be implemented but I'm lazy and this is just a test */
  75. return 0;
  76. }
  77.  
  78. static int spitest_resume(struct spi_device *spi)
  79. {
  80. /*should be implemented but I'm lazy and this is just a test */
  81. return 0;
  82. }
  83.  
  84. static struct spi_driver spi_test_driver = {
  85. .driver = {
  86. .name = "spitest",
  87. .bus = &spi_bus_type,
  88. .owner = THIS_MODULE,
  89. },
  90. .probe = spitest_probe,
  91. .remove = spitest_remove,
  92. .suspend = spitest_suspend,
  93. .resume = spitest_resume,
  94. };
  95.  
  96. static int __init spitest_init( void )
  97. {
  98. int ret = 0;
  99.  
  100. printk("Spitest module installing\n");
  101.  
  102. workqueue = create_workqueue("Spitest queue");
  103. if (workqueue == NULL){
  104. pr_err("%s: unable to create workqueue\n", __func__);
  105. ret = -1;
  106. goto out;
  107. }
  108.  
  109.  
  110. ret = spi_register_driver(&spi_test_driver);
  111. if (ret)
  112. pr_err("%s: problem at spi_register_driver\n", __func__);
  113.  
  114. out:
  115. return ret;
  116. }
  117.  
  118. static void __exit spitest_exit( void )
  119. {
  120. /*should be implemented but I'm lazy and this is just a test */
  121. }
  122.  
  123. MODULE_LICENSE("GPL");
  124. MODULE_AUTHOR("Tuomas Nylund");
  125. MODULE_DESCRIPTION("spitest");
  126. MODULE_VERSION("0.1");
  127.  
  128. module_init(spitest_init);
  129. module_exit(spitest_exit);

That thing there isn't enough by itself. To enable the device driver, we need to add some lines into a couple files.

build_dir/linux-ramips_rt305x/linux-3.3.8/drivers/misc/Kconfig

This file needs to be modified for us to be able to see the new driver in "make kernel_menuconfig" and enable it.
  1. [...]
  2. config SPITEST
  3. tristate "A simple SPI test"
  4. [...]

build_dir/linux-ramips_rt305x/linux-3.3.8/drivers/Kconfig

This file also needs to be modified for the driver to be compiled.
  1. [...]
  2. obj-$(CONFIG_SPITEST) += spitest.o
  3. [...]

build_dir/linux-ramips_rt305x/linux-3.3.8/arch/mips/ralink/rt305x/mach-carambola.c

This file provides support for the carambola board for the kernel. Among other things, it initializes the SPI. A couple of things need to be changed here. There is a struct in the file that tells how the SPI should be initialized. The member "max_speed_hz" needed changing; the original value of 0 resulted in a blazing clock speed of 50MHz, which was a bit too much for the shift register to keep up with. A new value of 2000000 provided a much more manageable clock speed of 2MHz. Also, I changed the "modalias" to something a bit more unique from the default value of "spidev".

  1. [...]
  2. static struct spi_board_info carambola_spi_info[] = {
  3. {
  4. .bus_num = 0,
  5. .chip_select = 0,
  6. .max_speed_hz = 2000000,
  7. .modalias = "spitest",
  8. }
  9. };
  10. [...]
After all of that, it's just a matter of compiling and uploading the fimware to the Carambola. I use the bootloader to download the new firmware with TFTP over ethernet. There's an article about it in the 
Carambola wiki.

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



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上



Ref : http://clc168.blogspot.tw/2007/08/linux-spi-driver-part-1.html

Ref :  http://dp.nonoo.hu/beaglebone-spi-driver/


Ref : http://linux-sunxi.org/SPIdev

Ref : http://tuomasnylund.fi/drupal6/content/making-embedded-linux-kernel-device-driver


Ref : https://www.kernel.org/doc/Documentation/spi/spidev


Ref : http://kezeodsnx.pixnet.net/blog/post/32024191-%5B%E8%BD%89%E8%B2%BC%5D-linux-spi-subsystem

1 則留言:

  1. 可以問一下 為什麼我建好一個空的 spi slave , /sys/class/spi_master找的到我建的spi9.0 但是我的/sys/class/spidev 裡面沒東西

    回覆刪除