Firefly开源社区

标题: spi驱动初试,在linux下实现的spiflash驱动 [打印本页]

作者: z3j6w9    时间: 2017-7-12 14:28
标题: spi驱动初试,在linux下实现的spiflash驱动
rk3288上还留有一个可以外接设备的spi0,所以我使用的是spi0,设备树文件如下,设备树中我没有指定CPOL和CPHA,在probe里手动赋值的

  1. static int flash_spi_probe(struct spi_device *spi)
  2. {
  3.         struct spidev_data        *spidev;
  4.         struct device_node  *np = spi->dev.of_node;   /*Get device tree node*/
  5.         int                                         status;
  6.         unsigned long                 minor;
  7.         int                  ret;
  8.         
  9.         printk("%s \n", __FUNCTION__);

  10.         /* Allocate driver data */
  11.         spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
  12.         if (!spidev)
  13.                 return -ENOMEM;

  14.         /* Initialize the driver data */
  15.         spi->mode = 1;
  16.         spi->max_speed_hz = 1000 * 1000 *10;
  17.         spi->bits_per_word = 8;
  18.         spidev->spi = spi;

  19.         /*inin lock*/
  20.         spin_lock_init(&spidev->spi_lock);
  21.         mutex_init(&spidev->buf_lock);

  22.         
  23.         /*Reset register condition*/
  24.         At45db_nrst = of_get_named_gpio(np, "At45db_nrst", 0);

  25.         ret = gpio_request(At45db_nrst, "At45db_nrst");
  26.         if (ret)
  27.                 return -ENODEV;
  28.         gpio_direction_output(At45db_nrst, GPIO_LOW);
  29.         gpio_direction_output(At45db_nrst, GPIO_HIGH);

  30.         /*Turn off write protect*/
  31.         At45db_nwp = of_get_named_gpio(np, "At45db_nwp", 0);
  32.         ret = gpio_request(At45db_nwp, "At45db_nwp");
  33.         if (ret)
  34.                 return -ENODEV;

  35.         //At45db_flash_init(spidev);

  36.         INIT_LIST_HEAD(&spidev->device_entry);

  37.         /* If we can allocate a minor number, hook up this device.
  38.          * Reusing minors is fine so long as udev or mdev is working.
  39.          */
  40.         mutex_lock(&device_list_lock);
  41.         minor = find_first_zero_bit(minors, N_SPI_MINORS);
  42.         if (minor < N_SPI_MINORS) {
  43.                 struct device *dev;

  44.                 spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
  45.                 dev = device_create(spiflash_cls, &spi->dev, spidev->devt,
  46.                                     spidev, "spidev%d.%d",
  47.                                     spi->master->bus_num, spi->chip_select);
  48.                 status = PTR_RET(dev);
  49.         } else {
  50.                 dev_dbg(&spi->dev, "no minor number available!\n");
  51.                 status = -ENODEV;
  52.         }
  53.         if (status == 0) {
  54.                 set_bit(minor, minors);
  55.                 list_add(&spidev->device_entry, &device_list);
  56.         }
  57.         mutex_unlock(&device_list_lock);

  58.         if (status == 0)
  59.                 spi_set_drvdata(spi, spidev);
  60.         else
  61.                 kfree(spidev);

  62.         return status;
  63. }
复制代码
  1. 和设备树匹配的代码,基本都是这个套路,匹配compatible就行了
复制代码
flash_spi.rar (3.24 KB, 下载次数: 44, 售价: 1 灯泡)

AT45DB041D.rar (707.62 KB, 下载次数: 32, 售价: 1 灯泡)








作者: z3j6w9    时间: 2017-7-12 14:36
本帖最后由 z3j6w9 于 2017-7-12 14:38 编辑

在probe中修改了max_frequence,是因为执行写操作报warning
spi_master spi0: DW SPI: Status keeps busy for 1000us after a read/write!
其实就是频率有点快,调慢点就行了

spi flash读数据前都需要写命令,刚开始先发命令再调用read获取数据,怎么都调不对波形,后来参看了下韦东山写的spi flash的代码,写成如下的形式就ok了;韦东山的spi视频是要花钱的,不过就19块,有兴趣的可以看看,我最近比较懒,所以就没去买来看了。
  1. static inline ssize_t
  2. spidev_sync_read(struct spidev_data *spidev, FLASH_C *flash_c)
  3. {
  4.         struct spi_transfer        t[] = {
  5.                 {
  6.                         .tx_buf                = flash_c->cmd,
  7.                         .len                = flash_c->cmd_len,
  8.                 },
  9.                 {
  10.                         .rx_buf     = flash_c->read_buf,
  11.                         .len        = flash_c->read_buf_len,
  12.                 }
  13.         };
  14.         struct spi_message        m;

  15.         spi_message_init(&m);
  16.         spi_message_add_tail(&t[0], &m);
  17.         spi_message_add_tail(&t[1], &m);
  18.         return spidev_sync(spidev, &m);
  19. }
复制代码

作者: z3j6w9    时间: 2017-7-12 14:42
本帖最后由 z3j6w9 于 2017-7-12 14:48 编辑

在对spi进行操作之前需要获取下当前芯片的状态,在初始化过程中操作是没有回应的。还有就是,我一开始在初始化后就尝试获取状态,因为调用的是read函数,结果就在如下代码中挂掉了,程序状态直接变成了D+,而且不可中断,重启无数次。
  1. static ssize_t
  2. spidev_sync(struct spidev_data *spidev, struct spi_message *message)
  3. {
  4.         DECLARE_COMPLETION_ONSTACK(done);
  5.         int status;

  6. #if 1
  7.         message->complete = spidev_complete;
  8.         message->context = &done;
  9. #endif
  10.         spin_lock_irq(&spidev->spi_lock);
  11.         if (spidev->spi == NULL)
  12.                 status = -ESHUTDOWN;
  13.         else
  14.                 status = spi_async(spidev->spi, message);
  15.         spin_unlock_irq(&spidev->spi_lock);

  16.         if (status == 0) {
  17.                 wait_for_completion(&done);
  18.                 status = message->status;
  19.                 if (status == 0)
  20.                         status = message->actual_length;
  21.         }
  22.         return status;
  23. }
复制代码


后续就改成了在每次读写执行获取芯片状态,不在锁里面做,防止死掉挂掉的时候会提示hung task,分享个别人写的定位的博客,其实写代码的过程中遇到问题是好事,真的能学会很多
http://blog.chinaunix.net/xmlrpc ... 10&uid=14528823

作者: z3j6w9    时间: 2017-7-12 14:45
本帖最后由 z3j6w9 于 2017-7-12 15:05 编辑


  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <fcntl.h>
  5. #include <assert.h>

  6. typedef struct TEST_Flash_Ctrl
  7. {         unsigned char *cmd;
  8.         unsigned char *read_buf;
  9.         unsigned char *w_data;
  10.         unsigned char  cmd_len;
  11.         unsigned char  read_buf_len;
  12.         unsigned char  w_len;
  13.         unsigned char  resv;
  14. }TEST_C;

  15. static int fd;
  16. static int write_to_flash(TEST_C *buf)
  17. {
  18.         return write(fd, buf, sizeof(TEST_C));
  19. }

  20. static int read_from_flash(TEST_C *buf)
  21. {
  22.         return read(fd, buf, buf->read_buf_len);
  23. }

  24. static int init_flash()
  25. {
  26.         /*erase before write*/
  27.         int ret ;
  28.         TEST_C buf;
  29.         unsigned char erase_cmd[] = {0xc7, 0x94, 0x80, 0x9a};

  30.         buf.cmd = erase_cmd;
  31.         buf.cmd_len = sizeof(erase_cmd);

  32.         ret = write_to_flash(&buf);
  33.         if (0 == ret)
  34.         {
  35.                 printf("%s: write err.\n ", __FUNCTION__);
  36.                 return ret;
  37.         }

  38.         return 0;
  39. }

  40. static int write_buffer(unsigned short addr, unsigned char* data, unsigned char len)
  41. {
  42.         unsigned char w_cmd[4];
  43.         unsigned char addr_u = (unsigned char)(addr >> 8);
  44.         unsigned char addr_v = (unsigned char)addr;
  45.         int ret;
  46.         TEST_C buf;

  47.         w_cmd[0] = 0x84;
  48.         w_cmd[1] = 0xff;
  49.         w_cmd[2] = addr_u;
  50.         w_cmd[3] = addr_v;

  51.         buf.cmd = w_cmd;
  52.         buf.cmd_len = sizeof(w_cmd);
  53.         buf.w_data = data;
  54.         buf.w_len  = len;

  55.         ret = write_to_flash(&buf);
  56.         if (0 == ret)
  57.         {
  58.                 printf("%s: write err.\n ", __FUNCTION__);
  59.                 return ret;
  60.         }

  61.         return 0;
  62. }

  63. static int read_buffer(unsigned short add, unsigned char *read_buf, unsigned char len)
  64. {

  65.         unsigned char r_cmd[5];
  66.         unsigned char addr_u;
  67.         unsigned char addr_v;
  68.         TEST_C test_c;
  69.         TEST_C buf;


  70.         addr_u = (unsigned char)(add >> 8);
  71.         addr_v = (unsigned char)add;

  72.         r_cmd[0] = 0xd4;
  73.         r_cmd[1] = 0xff;
  74.         r_cmd[2] = addr_u;
  75.         r_cmd[3] = addr_v;
  76.         r_cmd[4] = 0xff;

  77.         buf.cmd = r_cmd;
  78.         buf.cmd_len = sizeof(r_cmd);
  79.         buf.read_buf = read_buf;
  80.         buf.read_buf_len = len;

  81.         read_from_flash(&buf);
  82. }

  83. static unsigned char read_status()
  84. {
  85.         TEST_C buf;
  86.         unsigned char cmd[1];
  87.         unsigned char status = 0;
  88.         printf("Get status \n");

  89.         cmd[0] = 0xd7;

  90.         buf.cmd = cmd;
  91.         buf.cmd_len = 1;
  92.         buf.read_buf = &status;
  93.         buf.read_buf_len = 1;

  94.         read_from_flash(&buf);

  95.         return status;
  96. }

  97. int main(void)
  98. {
  99.         int ret = 0;
  100.         unsigned char buf_room[10];
  101.         unsigned char status;
  102.         unsigned short addr = 0x0;
  103.         unsigned char data[] = {0x00, 0xff, 0x0, 0xff, 0x0, 0xff};

  104.         fd = open("/dev/spidev0.0", O_RDWR);
  105.         if (fd < 0)
  106.         {
  107.                 printf("%s open device failed. \n", __FUNCTION__);
  108.                 return -1;
  109.         }
  110.         init_flash();
  111. #if 1
  112.         write_buffer(addr, data, sizeof(data));
  113.         read_buffer(addr, buf_room, 8);

  114.         printf("Data is %x \n", buf_room[0]);
  115.         printf("Data is %x \n", buf_room[1]);
  116.         printf("Data is %x \n", buf_room[2]);
  117. #endif
  118.         ret = close(fd);

  119.         return 0;
  120. }
复制代码

boxing.png (20.4 KB, 下载次数: 677)

boxing.png

作者: z3j6w9    时间: 2017-7-12 15:07
上面的是接收的数据,下面这张是发送的波形


作者: z3j6w9    时间: 2017-7-12 15:09
代码中只实现了对buffer的写/读,有兴趣研究嵌入式mcu开发的可以在论坛中私信我,大家私下一起交流技术
作者: aaaaa-ray    时间: 2017-7-21 11:24
请问自己新增一个档案当作driver,都需要修改dts档案吗??
作者: z3j6w9    时间: 2017-7-21 13:05
aaaaa-ray 发表于 2017-7-21 11:24
请问自己新增一个档案当作driver,都需要修改dts档案吗??

dts中写的是硬件相关的信息,如果你需要用到的硬件信息设备树中已经有了,那就不需要修改设备树了。设备树的出现其实就是替代了以前驱动中的board_info注册,将通用的硬件信息给整合了起来,既方便阅读,也方便开发。
作者: aaaaa-ray    时间: 2017-7-21 13:30
z3j6w9 发表于 2017-7-21 13:05
dts中写的是硬件相关的信息,如果你需要用到的硬件信息设备树中已经有了,那就不需要修改设备树了。设备 ...

太感谢解答
作者: kgp00213    时间: 2017-7-25 14:24
严重的感谢解答
作者: gn5969625    时间: 2017-12-18 15:08
感谢解答
作者: luokaisong130    时间: 2018-3-9 15:03
楼主,我最近入手了一块RK3328开发板,打算在预留的40pin插针上接一个spi设备,可是接线的时候发现压根就没有IRQ和RESET脚预留出来,管脚服用好像也不是太行,卡在这里了,请楼主点拨。附上原理图:
C:\Users\Administrator\Desktop\spi.png

spi.png (150.76 KB, 下载次数: 526)

spi.png

作者: z3j6w9    时间: 2018-3-18 10:06
luokaisong130 发表于 2018-3-9 15:03
楼主,我最近入手了一块RK3328开发板,打算在预留的40pin插针上接一个spi设备,可是接线的时候发现压根就没 ...

irq和reset都是可以接普通GPIO的,先来说说irq,所谓的中断,其实就是监测一个电位的变化,将GPIO设置为中断模式完全可以做到,reset就更简单了,通过程序控制电平即可,一般情况下很多芯片的RESET上面都有取反符号为低电平有效,所以程序运行时设置为高电平即可。
作者: luokaisong130    时间: 2018-3-21 16:17
z3j6w9 发表于 2018-3-18 10:06
irq和reset都是可以接普通GPIO的,先来说说irq,所谓的中断,其实就是监测一个电位的变化,将GPIO设置为 ...

谢谢大神,我后面也是这么做的,第一次玩嵌入式是小白,哈哈
作者: chnchmhgw    时间: 2018-5-30 10:24
楼主,你好,我参考你的代码,在aio-3288j主板上进行测试,代码编译成功,/dev目录下也产生了设备节点;我使用的是aio-3288j主板上真实的spi2,用逻辑分析仪抓取波形,没有输出;
我初步推断,可能是At45db_nwp和At45db_nrst的gpio设置问题,这个需要怎么设置?给些指点,谢谢!
我的aio-3288j主板dts中spi2设置见图1,spi2的管脚见图2

捕获.PNG (8.92 KB, 下载次数: 611)

图1

图1

捕获1.PNG (25.12 KB, 下载次数: 567)

图2

图2

作者: 阿发君    时间: 2020-10-28 14:51
mark




欢迎光临 Firefly开源社区 (https://dev.t-firefly.com/) Powered by Discuz! X3.1