前言
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的代码时,会发现更有意思的东西:
-
-
-
-
-
-
-
-
- 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中实现:
-
-
-
- void device_shutdown(void)
- {
- struct device *dev, *parent;
-
- spin_lock(&devices_kset->list_lock);
-
-
-
-
-
- while (!list_empty(&devices_kset->list)) {
- dev = list_entry(devices_kset->list.prev, struct device,
- kobj.entry);
-
-
-
-
-
-
- parent = get_device(dev->parent);
- get_device(dev);
-
-
-
-
- list_del_init(&dev->kobj.entry);
- spin_unlock(&devices_kset->list_lock);
-
-
- if (parent)
- device_lock(parent);
- device_lock(dev);
-
-
- pm_runtime_get_noresume(dev);
- pm_runtime_barrier(dev);
-
-
-
-
- if (dev->bus && dev->bus->shutdown) {
-
- dev_info(dev, "shutdown\n");
- dev->bus->shutdown(dev);
- } else if (dev->driver && dev->driver->shutdown) {
-
- 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);
-
-
- flash->command[0] = 0x66;
- spi_write(flash->spi, flash->command, 1);
- flash->command[0] = 0x99;
- spi_write(flash->spi, flash->command, 1);
-
- 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,
-
- .shutdown = m25p_remove,
-
-
-
-
-
- };
总结
通过注册设备的shutdown方法,让我们有机会在系统重启的时刻,做一些deinit的操作.通过此种方法来复位spi flash,优雅而简洁.