让MT7620完美支持32M SPI Flash(W25Q256) — 兼谈设备驱动中的shutdown方法

前言

OpenWrt的最新kernel(3.14.28)已经能够支持32M SPI Flash的读写以及擦除操作.然而,可能是系统考虑不周,亦或是MT7620系统的BUG,在配置了W25Q256的MT7620开发板系统上,无法soft reset!经过查阅相关资料,发现,MT7620默认支持24bit(3byte)的spi地址模式,而要支持32M以上的spi flash,则必须切换到32bit(4byte)地址模式.在soft reset的时候,spi停留在了32bit模式,没有切换回默认的24bit模式,导致reset后,MT7620在默认的24bit模式,无法和32bit模式的spi通讯,系统死机.那么问题来了:如何在soft reset时刻,让spi flash切换回24bit模式呢?本文通过设备驱动中的一个shutdown方法来解决这个问题.


背景知识

在linux源代码kernel目录下,有一个reboot.c文件,里面暴露了一个register_reboot_notifier方法,可以让kernel中的代码有机会获得reboot的通知,当我们继续分析reboot.c的代码时,会发现更有意思的东西:

[cpp]  view plain  copy
 print ?
  1. /** 
  2.  *  kernel_restart - reboot the system 
  3.  *  @cmd: pointer to buffer containing command to execute for restart 
  4.  *      or %NULL 
  5.  * 
  6.  *  Shutdown everything and perform a clean reboot. 
  7.  *  This is not safe to call in interrupt context. 
  8.  */  
  9. void kernel_restart(char *cmd)  
  10. {  
  11.     kernel_restart_prepare(cmd);  
  12.     migrate_to_reboot_cpu();  
  13.     syscore_shutdown();  
  14.     if (!cmd)  
  15.         pr_emerg("Restarting system\n");  
  16.     else  
  17.         pr_emerg("Restarting system with command '%s'\n", cmd);  
  18.     kmsg_dump(KMSG_DUMP_RESTART);  
  19.     machine_restart(cmd);  
  20. }  
在kernel_restart中,又调用了kernel_restart_prepare方法:

[cpp]  view plain  copy
 print ?
  1. void kernel_restart_prepare(char *cmd)  
  2. {  
  3.     blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd);  
  4.     system_state = SYSTEM_RESTART;  
  5.     usermodehelper_disable();  
  6.     device_shutdown();  
  7. }  
device_shutdown在drivers/base/core.c中实现:

[cpp]  view plain  copy
 print ?
  1. /** 
  2.  * device_shutdown - call ->shutdown() on each device to shutdown. 
  3.  */  
  4. void device_shutdown(void)  
  5. {  
  6.     struct device *dev, *parent;  
  7.   
  8.     spin_lock(&devices_kset->list_lock);  
  9.     /* 
  10.      * Walk the devices list backward, shutting down each in turn. 
  11.      * Beware that device unplug events may also start pulling 
  12.      * devices offline, even as the system is shutting down. 
  13.      */  
  14.     while (!list_empty(&devices_kset->list)) {  
  15.         dev = list_entry(devices_kset->list.prev, struct device,  
  16.                 kobj.entry);  
  17.   
  18.         /* 
  19.          * hold reference count of device's parent to 
  20.          * prevent it from being freed because parent's 
  21.          * lock is to be held 
  22.          */  
  23.         parent = get_device(dev->parent);  
  24.         get_device(dev);  
  25.         /* 
  26.          * Make sure the device is off the kset list, in the 
  27.          * event that dev->*->shutdown() doesn't remove it. 
  28.          */  
  29.         list_del_init(&dev->kobj.entry);  
  30.         spin_unlock(&devices_kset->list_lock);  
  31.   
  32.         /* hold lock to avoid race with probe/release */  
  33.         if (parent)  
  34.             device_lock(parent);  
  35.         device_lock(dev);  
  36.   
  37.         /* Don't allow any more runtime suspends */  
  38.         pm_runtime_get_noresume(dev);  
  39.         pm_runtime_barrier(dev);  
  40.   
  41.         // manfeel, add debug info  
  42.         //dev_info(dev,"search shutdown method...\n");  
  43.           
  44.         if (dev->bus && dev->bus->shutdown) {  
  45.             //if (initcall_debug) manfeel  
  46.                 dev_info(dev, "shutdown\n");  
  47.             dev->bus->shutdown(dev);  
  48.         } else if (dev->driver && dev->driver->shutdown) {  
  49.             //if (initcall_debug) manfeel  
  50.                 dev_info(dev, "shutdown\n");  
  51.             dev->driver->shutdown(dev);  
  52.         }  
  53.   
  54.         device_unlock(dev);  
  55.         if (parent)  
  56.             device_unlock(parent);  
  57.   
  58.         put_device(dev);  
  59.         put_device(parent);  
  60.   
  61.         spin_lock(&devices_kset->list_lock);  
  62.     }  
  63.     spin_unlock(&devices_kset->list_lock);  
  64.     async_synchronize_full();  
  65. }  
通过阅读代码,我们不难发现,在device_shutdown中,枚举了设备的shutdown方法,如果存在该方法,则会调用之.

于是,32M spi flash的reset方法喷薄而出.


解决办法

转到drivers/mtd/devices/m25p80.c

修改如下代码:

[cpp]  view plain  copy
 print ?
  1. static int m25p_remove(struct spi_device *spi)  
  2. {  
  3.     struct m25p *flash = spi_get_drvdata(spi);  
  4.       
  5.     // manfeel note: add spi flash reset code  
  6.     flash->command[0] = 0x66;  
  7.     spi_write(flash->spi, flash->command, 1);  
  8.     flash->command[0] = 0x99;  
  9.     spi_write(flash->spi, flash->command, 1);  
  10.     /* Clean up MTD stuff. */  
  11.     return mtd_device_unregister(&flash->mtd);  
  12. }  
  13.   
  14.   
  15. static struct spi_driver m25p80_driver = {  
  16.     .driver = {  
  17.         .name   = "m25p80",  
  18.         .owner  = THIS_MODULE,  
  19.     },  
  20.     .id_table   = m25p_ids,  
  21.     .probe  = m25p_probe,  
  22.     .remove = m25p_remove,  
  23.     // manfeel, add shutdown method to reset spi flash  
  24.     .shutdown = m25p_remove,  
  25.   
  26.     /* REVISIT: many of these chips have deep power-down modes, which 
  27.      * should clearly be entered on suspend() to minimize power use. 
  28.      * And also when they're otherwise idle... 
  29.      */  
  30. };  


总结

通过注册设备的shutdown方法,让我们有机会在系统重启的时刻,做一些deinit的操作.通过此种方法来复位spi flash,优雅而简洁.

你可能感兴趣的:(MTK)