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

来源:互联网 发布:无线访客网络 编辑:程序博客网 时间:2024/05/21 13:22

让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的代码时,会发现更有意思的东西:

    /**      *  kernel_restart - reboot the system      *  @cmd: pointer to buffer containing command to execute for restart      *      or %NULL      *      *  Shutdown everything and perform a clean reboot.      *  This is not safe to call in interrupt context.      */      void kernel_restart(char *cmd)      {          kernel_restart_prepare(cmd);          migrate_to_reboot_cpu();          syscore_shutdown();          if (!cmd)              pr_emerg("Restarting system\n");          else              pr_emerg("Restarting system with command '%s'\n", cmd);          kmsg_dump(KMSG_DUMP_RESTART);          machine_restart(cmd);      }  

在kernel_restart中,又调用了kernel_restart_prepare方法:

    void kernel_restart_prepare(char *cmd)      {          blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd);          system_state = SYSTEM_RESTART;          usermodehelper_disable();          device_shutdown();      }  

device_shutdown在drivers/base/core.c中实现:

    /**      * device_shutdown - call ->shutdown() on each device to shutdown.      */      void device_shutdown(void)      {          struct device *dev, *parent;          spin_lock(&devices_kset->list_lock);          /*          * Walk the devices list backward, shutting down each in turn.          * Beware that device unplug events may also start pulling          * devices offline, even as the system is shutting down.          */          while (!list_empty(&devices_kset->list)) {              dev = list_entry(devices_kset->list.prev, struct device,                      kobj.entry);              /*              * hold reference count of device's parent to              * prevent it from being freed because parent's              * lock is to be held              */              parent = get_device(dev->parent);              get_device(dev);              /*              * Make sure the device is off the kset list, in the              * event that dev->*->shutdown() doesn't remove it.              */              list_del_init(&dev->kobj.entry);              spin_unlock(&devices_kset->list_lock);              /* hold lock to avoid race with probe/release */              if (parent)                  device_lock(parent);              device_lock(dev);              /* Don't allow any more runtime suspends */              pm_runtime_get_noresume(dev);              pm_runtime_barrier(dev);              // manfeel, add debug info              //dev_info(dev,"search shutdown method...\n");              if (dev->bus && dev->bus->shutdown) {                  //if (initcall_debug) manfeel                      dev_info(dev, "shutdown\n");                  dev->bus->shutdown(dev);              } else if (dev->driver && dev->driver->shutdown) {                  //if (initcall_debug) manfeel                      dev_info(dev, "shutdown\n");                  dev->driver->shutdown(dev);              }              device_unlock(dev);              if (parent)                  device_unlock(parent);              put_device(dev);              put_device(parent);              spin_lock(&devices_kset->list_lock);          }          spin_unlock(&devices_kset->list_lock);          async_synchronize_full();      }  

通过阅读代码,我们不难发现,在device_shutdown中,枚举了设备的shutdown方法,如果存在该方法,则会调用之.

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

解决办法

转到drivers/mtd/devices/m25p80.c

修改如下代码:

    static int m25p_remove(struct spi_device *spi)      {          struct m25p *flash = spi_get_drvdata(spi);          // manfeel note: add spi flash reset code          flash->command[0] = 0x66;          spi_write(flash->spi, flash->command, 1);          flash->command[0] = 0x99;          spi_write(flash->spi, flash->command, 1);          /* Clean up MTD stuff. */          return mtd_device_unregister(&flash->mtd);      }      static struct spi_driver m25p80_driver = {          .driver = {              .name   = "m25p80",              .owner  = THIS_MODULE,          },          .id_table   = m25p_ids,          .probe  = m25p_probe,          .remove = m25p_remove,          // manfeel, add shutdown method to reset spi flash          .shutdown = m25p_remove,          /* REVISIT: many of these chips have deep power-down modes, which          * should clearly be entered on suspend() to minimize power use.          * And also when they're otherwise idle...          */      };  

总结

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

原创粉丝点击