1. 程式人生 > >linux 支援32M SPI Flash(W25Q256) — 兼談裝置驅動中的shutdown方法

linux 支援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,優雅而簡潔.