qspi flash驱动开发记录

毕业也有两年了,从大一接触嵌入式以来快六年了,这六年学到的东西也有很多。以前都是看别人的博客,自有写博客想法以来也有一段时间,此博客作为博客的开端,接下来的工作学习中会把所见所学记录在博客中。希望以后能够多写博客,很多疏漏的地方还望大家指正,共同进步

驱动概括

以前在学校大多数嵌入式都是裸机跑起来,驱动非常简单;然后就是一些简单的嵌入式操作系统,比如说freeRtos,这些也都是裸机的驱动;linux驱动就比较复杂,有它自己的驱动框架,并且这个框架十分庞大。这个linux驱动程序就是:

裸机驱动 + linux驱动框架

即按照linux框架来组织驱动程序,所以学好裸机驱动之后,就需要画大功夫去学习linux驱动框架

驱动框架演变

1,设备和驱动在一起

设备相关的信息,比如寄存器地址、大小等信息都是在一个源文件中;以韦东山视频中最简单的led程序为例耦合性比较强。

2,设备和驱动分离

设备和驱动分离开来,设备信息放在一个文件中,驱动程序放在一个文件中;两个文件分别加载,在内核启动的时候根据名字匹配,匹配成功后就调用probe()函数,进行相关的初始化。

3,设备树的引入

由于之前的设备和驱动都是编译进内核中,且没修改一下设备地址就需要重新编译一下内核。并且平台信息太乱,所以后面就把设备信息单独放到一个文件中以文本的形式组织起来。并且单独编译,不与内核一起编译

设备树

1)前面的总线设备驱动模型中,硬件资源来自于leds_dev.c里面的信息,这样会导致不同的板子,会添加不同的硬件资源信息,造成内核的臃肿。
2)使用设备树后,内核不再包含硬件的描述,硬件描述放在单独的DTS里面,然后编译成二进制的DTB,在U-Boot启动的时候加载进去,然后内核进行解析。
3)DTS、DTC和DTB之间的关系:
DTS经过DTC编译得到DTB,DTB通过DTC反编译得到DTS.
4)ARM中,所有的DTS文件放在arch/arm/boot/dts目录中,为了简化,将Soc公用部分提取了出来作为dtsi,类似头文件。
5)DTC编译工具的源代码在scripts/dtc目录中,编译内核时,编译内核时,需要使能才能将源码编译成工具,对应于scripts/dtc/Makefile中"hostprogs-y:=dtc"。Ubuntu也可直接安装DTC工具:

例:

/include/ “skeleton.dtsi”
/ {
compatible = “xlnx,zynq-7000”;

cpus {
	#address-cells = <1>;
	#size-cells = <0>;

	cpu@0 {
		compatible = "arm,cortex-a9";
		device_type = "cpu";
		reg = <0>;
		clocks = <&clkc 3>;
		clock-latency = <1000>;
		cpu0-supply = <&regulator_vccpint>;
		operating-points = <
			/* kHz    uV */
			666667  1000000
			333334  1000000
		>;
	};

	cpu@1 {
		compatible = "arm,cortex-a9";
		device_type = "cpu";
		reg = <1>;
		clocks = <&clkc 3>;
	};
};

pmu {
	compatible = "arm,cortex-a9-pmu";
	interrupts = <0 5 4>, <0 6 4>;
	interrupt-parent = <&intc>;
	reg = < 0xf8891000 0x1000 0xf8893000 0x1000 >;
};

regulator_vccpint: fixedregulator@0 {
	compatible = "regulator-fixed";
	regulator-name = "VCCPINT";
	regulator-min-microvolt = <1000000>;
	regulator-max-microvolt = <1000000>;
	regulator-boot-on;
	regulator-always-on;
};

amba: amba {
	compatible = "simple-bus";
	#address-cells = <1>;
	#size-cells = <1>;
	interrupt-parent = <&intc>;
	ranges;

	adc: adc@f8007100 {
		compatible = "xlnx,zynq-xadc-1.00.a";
		reg = <0xf8007100 0x20>;
		interrupts = <0 7 4>;
		interrupt-parent = <&intc>;
		clocks = <&clkc 12>;
	};

	can0: can@e0008000 {
		compatible = "xlnx,zynq-can-1.0";
		status = "disabled";
		clocks = <&clkc 19>, <&clkc 36>;
		clock-names = "can_clk", "pclk";
		reg = <0xe0008000 0x1000>;
		interrupts = <0 28 4>;
		interrupt-parent = <&intc>;
		tx-fifo-depth = <0x40>;
		rx-fifo-depth = <0x40>;
	};

	can1: can@e0009000 {
		compatible = "xlnx,zynq-can-1.0";
		status = "disabled";
		clocks = <&clkc 20>, <&clkc 37>;
		clock-names = "can_clk", "pclk";
		reg = <0xe0009000 0x1000>;
		interrupts = <0 51 4>;
		interrupt-parent = <&intc>;
		tx-fifo-depth = <0x40>;
		rx-fifo-depth = <0x40>;
	};

	gpio0: gpio@e000a000 {
		compatible = "xlnx,zynq-gpio-1.0";
		#gpio-cells = <2>;
		#interrupt-cells = <2>;
		clocks = <&clkc 42>;
		gpio-controller;
		interrupt-controller;
		interrupt-parent = <&intc>;
		interrupts = <0 20 4>;
		reg = <0xe000a000 0x1000>;
	};

	i2c0: i2c@e0004000 {
		compatible = "cdns,i2c-r1p10";
		status = "disabled";
		clocks = <&clkc 38>;
		interrupt-parent = <&intc>;
		interrupts = <0 25 4>;
		reg = <0xe0004000 0x1000>;
		#address-cells = <1>;
		#size-cells = <0>;
	};

	i2c1: i2c@e0005000 {
		compatible = "cdns,i2c-r1p10";
		status = "disabled";
		clocks = <&clkc 39>;
		interrupt-parent = <&intc>;
		interrupts = <0 48 4>;
		reg = <0xe0005000 0x1000>;
		#address-cells = <1>;
		#size-cells = <0>;
	};

	intc: interrupt-controller@f8f01000 {
		compatible = "arm,cortex-a9-gic";
		#interrupt-cells = <3>;
		interrupt-controller;
		reg = <0xF8F01000 0x1000>,
		      <0xF8F00100 0x100>;
	};

	L2: cache-controller@f8f02000 {
		compatible = "arm,pl310-cache";
		reg = <0xF8F02000 0x1000>;
		interrupts = <0 2 4>;
		arm,data-latency = <3 2 2>;
		arm,tag-latency = <2 2 2>;
		cache-unified;
		cache-level = <2>;
	};

	mc: memory-controller@f8006000 {
		compatible = "xlnx,zynq-ddrc-a05";
		reg = <0xf8006000 0x1000>;
	};

	ocmc: ocmc@f800c000 {
		compatible = "xlnx,zynq-ocmc-1.0";
		interrupt-parent = <&intc>;
		interrupts = <0 3 4>;
		reg = <0xf800c000 0x1000>;
	};

	uart0: serial@e0000000 {
		compatible = "xlnx,xuartps", "cdns,uart-r1p8";
		status = "disabled";
		clocks = <&clkc 23>, <&clkc 40>;
		clock-names = "uart_clk", "pclk";
		reg = <0xE0000000 0x1000>;
		interrupts = <0 27 4>;
	};

	uart1: serial@e0001000 {
		compatible = "xlnx,xuartps", "cdns,uart-r1p8";
		status = "disabled";
		clocks = <&clkc 24>, <&clkc 41>;
		clock-names = "uart_clk", "pclk";
		reg = <0xE0001000 0x1000>;
		interrupts = <0 50 4>;
	};

	spi0: spi@e0006000 {
		compatible = "xlnx,zynq-spi-r1p6";
		reg = <0xe0006000 0x1000>;
		status = "disabled";
		interrupt-parent = <&intc>;
		interrupts = <0 26 4>;
		clocks = <&clkc 25>, <&clkc 34>;
		clock-names = "ref_clk", "pclk";
		#address-cells = <1>;
		#size-cells = <0>;
	};

	spi1: spi@e0007000 {
		compatible = "xlnx,zynq-spi-r1p6";
		reg = <0xe0007000 0x1000>;
		status = "disabled";
		interrupt-parent = <&intc>;
		interrupts = <0 49 4>;
		clocks = <&clkc 26>, <&clkc 35>;
		clock-names = "ref_clk", "pclk";
		#address-cells = <1>;
		#size-cells = <0>;
	};

	qspi: spi@e000d000 {
		clock-names = "ref_clk", "pclk";
		clocks = <&clkc 10>, <&clkc 43>;
		compatible = "xlnx,zynq-qspi-1.0";
		status = "disabled";
		interrupt-parent = <&intc>;
		interrupts = <0 19 4>;
		reg = <0xe000d000 0x1000>;
		#address-cells = <1>;
		#size-cells = <0>;
	};

	smcc: memory-controller@e000e000 {
		#address-cells = <1>;
		#size-cells = <1>;
		status = "disabled";
		clock-names = "memclk", "aclk";
		clocks = <&clkc 11>, <&clkc 44>;
		compatible = "arm,pl353-smc-r2p1";
		interrupt-parent = <&intc>;
		interrupts = <0 18 4>;
		ranges ;
		reg = <0xe000e000 0x1000>;
		nand0: flash@e1000000 {
			status = "disabled";
			compatible = "arm,pl353-nand-r2p1";
			reg = <0xe1000000 0x1000000>;
			#address-cells = <0x1>;
			#size-cells = <0x1>;
		};
		nor0: flash@e2000000 {
			status = "disabled";
			compatible = "cfi-flash";
			reg = <0xe2000000 0x2000000>;
			#address-cells = <1>;
			#size-cells = <1>;
		};
	};

	gem0: ethernet@e000b000 {
		compatible = "cdns,zynq-gem", "cdns,gem";
		reg = <0xe000b000 0x1000>;
		status = "disabled";
		interrupts = <0 22 4>;
		clocks = <&clkc 30>, <&clkc 30>, <&clkc 13>;
		clock-names = "pclk", "hclk", "tx_clk";
		#address-cells = <1>;
		#size-cells = <0>;
	};

	gem1: ethernet@e000c000 {
		compatible = "cdns,zynq-gem", "cdns,gem";
		reg = <0xe000c000 0x1000>;
		status = "disabled";
		interrupts = <0 45 4>;
		clocks = <&clkc 31>, <&clkc 31>, <&clkc 14>;
		clock-names = "pclk", "hclk", "tx_clk";
		#address-cells = <1>;
		#size-cells = <0>;
	};

	sdhci0: sdhci@e0100000 {
		compatible = "arasan,sdhci-8.9a";
		status = "disabled";
		clock-names = "clk_xin", "clk_ahb";
		clocks = <&clkc 21>, <&clkc 32>;
		interrupt-parent = <&intc>;
		interrupts = <0 24 4>;
		reg = <0xe0100000 0x1000>;
	};

	sdhci1: sdhci@e0101000 {
		compatible = "arasan,sdhci-8.9a";
		status = "disabled";
		clock-names = "clk_xin", "clk_ahb";
		clocks = <&clkc 22>, <&clkc 33>;
		interrupt-parent = <&intc>;
		interrupts = <0 47 4>;
		reg = <0xe0101000 0x1000>;
	};

	slcr: slcr@f8000000 {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "xlnx,zynq-slcr", "syscon", "simple-bus";
		reg = <0xF8000000 0x1000>;
		ranges;
		clkc: clkc@100 {
			#clock-cells = <1>;
			compatible = "xlnx,ps7-clkc";
			fclk-enable = <0xf>;
			clock-output-names = "armpll", "ddrpll", "iopll", "cpu_6or4x",
					"cpu_3or2x", "cpu_2x", "cpu_1x", "ddr2x", "ddr3x",
					"dci", "lqspi", "smc", "pcap", "gem0", "gem1",
					"fclk0", "fclk1", "fclk2", "fclk3", "can0", "can1",
					"sdio0", "sdio1", "uart0", "uart1", "spi0", "spi1",
					"dma", "usb0_aper", "usb1_aper", "gem0_aper",
					"gem1_aper", "sdio0_aper", "sdio1_aper",
					"spi0_aper", "spi1_aper", "can0_aper", "can1_aper",
					"i2c0_aper", "i2c1_aper", "uart0_aper", "uart1_aper",
					"gpio_aper", "lqspi_aper", "smc_aper", "swdt",
					"dbg_trc", "dbg_apb";
			reg = <0x100 0x100>;
		};

		rstc: rstc@200 {
			compatible = "xlnx,zynq-reset";
			reg = <0x200 0x48>;
			#reset-cells = <1>;
			syscon = <&slcr>;
		};

		pinctrl0: pinctrl@700 {
			compatible = "xlnx,pinctrl-zynq";
			reg = <0x700 0x200>;
			syscon = <&slcr>;
		};
	};

	dmac_s: dmac@f8003000 {
		compatible = "arm,pl330", "arm,primecell";
		reg = <0xf8003000 0x1000>;
		interrupt-parent = <&intc>;
		interrupt-names = "abort", "dma0", "dma1", "dma2", "dma3",
			"dma4", "dma5", "dma6", "dma7";
		interrupts = <0 13 4>,
				<0 14 4>, <0 15 4>,
				<0 16 4>, <0 17 4>,
				<0 40 4>, <0 41 4>,
				<0 42 4>, <0 43 4>;
		#dma-cells = <1>;
		#dma-channels = <8>;
		#dma-requests = <4>;
		clocks = <&clkc 27>;
		clock-names = "apb_pclk";
	};

	devcfg: devcfg@f8007000 {
		clock-names = "ref_clk", "fclk0", "fclk1", "fclk2", "fclk3";
		clocks = <&clkc 12>, <&clkc 15>, <&clkc 16>, <&clkc 17>, <&clkc 18>;
		compatible = "xlnx,zynq-devcfg-1.0";
		interrupt-parent = <&intc>;
		interrupts = <0 8 4>;
		reg = <0xf8007000 0x100>;
		syscon = <&slcr>;
	};

	efuse: efuse@f800d000 {
		compatible = "xlnx,zynq-efuse";
		reg = <0xf800d000 0x20>;
	};

	global_timer: timer@f8f00200 {
		compatible = "arm,cortex-a9-global-timer";
		reg = <0xf8f00200 0x20>;
		interrupts = <1 11 0x301>;
		interrupt-parent = <&intc>;
		clocks = <&clkc 4>;
	};

	ttc0: timer@f8001000 {
		interrupt-parent = <&intc>;
		interrupts = <0 10 4>, <0 11 4>, <0 12 4>;
		compatible = "cdns,ttc";
		clocks = <&clkc 6>;
		reg = <0xF8001000 0x1000>;
	};

	ttc1: timer@f8002000 {
		interrupt-parent = <&intc>;
		interrupts = <0 37 4>, <0 38 4>, <0 39 4>;
		compatible = "cdns,ttc";
		clocks = <&clkc 6>;
		reg = <0xF8002000 0x1000>;
	};

	scutimer: timer@f8f00600 {
		interrupt-parent = <&intc>;
		interrupts = <1 13 0x301>;
		compatible = "arm,cortex-a9-twd-timer";
		reg = <0xf8f00600 0x20>;
		clocks = <&clkc 4>;
	};

	usb0: usb@e0002000 {
		compatible =  "xlnx,zynq-usb-1.00.a", "chipidea,usb2";
		status = "disabled";
		clocks = <&clkc 28>;
		interrupt-parent = <&intc>;
		interrupts = <0 21 4>;
		reg = <0xe0002000 0x1000>;
		phy_type = "ulpi";
	};

	usb1: usb@e0003000 {
		compatible =  "xlnx,zynq-usb-1.00.a", "chipidea,usb2";
		status = "disabled";
		clocks = <&clkc 29>;
		interrupt-parent = <&intc>;
		interrupts = <0 44 4>;
		reg = <0xe0003000 0x1000>;
		phy_type = "ulpi";
	};

	watchdog0: watchdog@f8005000 {
		clocks = <&clkc 45>;
		compatible = "cdns,wdt-r1p2";
		interrupt-parent = <&intc>;
		interrupts = <0 9 1>;
		reg = <0xf8005000 0x1000>;
		timeout-sec = <10>;
	};
};

};

qspi flash预备知识

由于在工作中使用的事xilinx 的zynq平台,公司的产品也是基于此平台,以后的驱动学习也是基于此平台。

zynq qspi flash controller介绍

1,概括
控制器有三种操作模式:I/O模式;线性地址模式;原始的SPI模式。
(1)在I/O模式,使用flash设备协议来交互;软件使用四个TXD寄存器来写flash命令以及数据,通过读RXD寄存器来接受flash数据。
(2)线性地址模式使用一个设备子集来减少软件开销,在I/O模式下需要软件需要去读取flash存储器。线性模式使用硬件去处理给flash存储器的命令以及控制flash设备到AXI接口的数据流传输。这个控制器让AXI接口访问flash 存储器像访问ROM存储器一样。
(3)原始spi模式:QSPI控制器表现的跟普通SPI控制器一样。
1.1,特点

  • 线性地址模式传输的32-bit AXI接口
  • I/O模式传输的32-bit APB接口
  • 可编程的来自micron和spansion公司的flash储存器总线协议。
  • 普通spi模式以及可扩展的性能:1x,2x,4x,8x I/O宽度
  • 灵活的I/O
    • 单 SS 4位flash存储器接口模式
    • 双 SS 8位并行1/O存储器接口模式
    • 双SS 4位堆叠I/O存储器模式
    • 单SS,原始SPI模式
  • 每个设备16MB地址(两个设备32MB)
  • 设备容量高达128Mb
  • I/O 模式(flash控制和数据)
    • 软件处理指令以及管理flash操作
    • FIFO控制中断
    • 63-word接受fifo,63-word发送fifo
  • 线性地址模式(可执行的读访问)
    • 存储器的读与写是通过控制器来执行的
    • 多达4个读请求的AXI portbuffer
    • AXI递增和封装地址函数

1.2,系统图
系统图
线性地址模式下的地址映射和设备匹配

当只连接一个设备的时候直接地址操作映射是从FC000000到最大FCFFFFFF(16MB)。连接两个设备的地址映射取决于存储器设备以及I/O配置。在两个设备系统中,QSPI设备需要来自相同的供应商,这样他们的协议才会是一样的。
八位并行I/O模式也需要设备具有相同的容量,地址操作映射是从FC000000到最大FDFFFFFF(32MB)
四位stacked I/O模式,设备可以有不通的容量,但是必须是相同的协议。如果使用了两个不通容量的设备,xilinx推荐使用一个128Mb的设备在低地址。这种模式下,QSPI 0 是前16MB,QSPI是后16MB。如果第一设备小于16MB,那么在两个设备之间就会有地址空缺
模块框图
在这里插入图片描述

1.4 操作限制
当只连接一个设备时,必须连接在QSPI0,当使用两个设备时,两个设备必须是一样的(相同的供应商、相同的协议)
QSPI控制器的MIO引脚与NOR/NAND的控制器引脚是冲突的,当QSPI被使用的时候NOR/NAND接口就不能被使用

2,功能描述
QSPI flash控制器既可以工作在I/O模式下也可以工作在线性地址模式。对于读取数据,控制器在I/O模式以及线性地址模式都支持singal、dual以及quad读模式。对于写入数据,signal和quad模式是在I/O模式下支持,而在线性地址模式下不支持写入数据。
2.1,操作模式
在这里插入图片描述
在I/O模式下,软件可以通过设置对应的寄存器位来选择各种操作方式。在线性地址模式下,控制器执行左右必要的读数据管理,软件使用就跟ROM一样

2.1,I/O模式
在I/O模式下,命令和数据的指令是根据QSPI的协议通过软件来实现的。这个不同格式的命令是排列好的,其中包括指令和数据,之后通过重复写TXD寄存器来把这些命令传递到TxFIFO中去。这个传输逻辑电路按照QSPI接口的说明连载TxFIFO的内容,然后发送数据到flash储存器,当传输逻辑在传输数据的时候也会采集接受的数据,并保存在TxFIFO中。
在读取命令的情况下,数据在命令和地址之后被传递,这个MIO在传输逻辑的控制下在恰当的时候从输出转变为输入。数据移位到RxFIFO中既有有效数据也有无用的数据
软件需要过滤RxFIFO的原始数据来获得有用的数据,控制器即修改软件写入指令得到数据也不修改读取命令返回的数据。
控制器支持小段模式,最小字节的最小位首先被传递。
数据流控制
I/O模式下载数据传输中不同种数据流控制。用户需要选择自动模式还是手动模式,通过控制 config_reg.MANSTARTEN (Man_start_com).在手动模式下,用户能够进一步悬着手动或者自动芯片选择,通过控制Config_reg.SSFORCE (Manaual_CS).声明芯片选择信号在命令流的开始阶段。在CS有效之后,DO数据是给flash 存储器的命令
在自动模式下,整个传输序列,包括芯片使能引脚的控制都是硬件自动完成的。期间不需要软件的参与。通过写TxD数据到TxFIFO之后,传输立刻开始,芯片选择自动有效。当TxFIFO空之后传输完成,芯片选择信号自动无效。在这个模式下,为了实现连续的数据传输,软件写TxFIFO的速率必须大于等于MIO传输数据的速率。这将是困难的,因为读取RXD数据和写TXD数据是在APB时钟的速率下。
在手动模式下,用户控制数据的传输。在这种情况下,软件需要些整个传输队列到TxFIFO直到TxFIFO满了。通过写Man_start_en位,控制器开始工作,使能CS,开始吧TxFIFO移出,同时移入数据到RxFIFO,正确的控制MIO的输入/输出状态,传输整个对流直到TxFIFO为空然后使CS无效,每个命令最大的数据队列受TxFIFO深度的影响是252bytes。
在手动模式下,用户可以在通过控制开始传输之外控制芯片使能、软件之后写入传输队列,软件使能CS,但是在传输完成之后,CS在TxFIFO空了之后不会使能无效。软件可以继续填写之前没有写完的命令数据到TxFIFO中。这种方法消除了前面的单个命令长度的限制可以单次传输非常多的数据。在一个命令传输完成之后通过写Manual_CS位来软件失能CS信号。
** I/O模式传输寄存器(TXD)**
软件写flash 设备需要的数据流,根据QSPI设备厂商的说明。控制器有4个32-bit只写寄存器,这些寄存器用来写命令数据去获得状态或者读写存储器中的数据,Qspi TXD寄存器的写入形式如下图标所示,写这四个寄存器最后都会写入到TxFIFO当中去
用户必须在一下连续访问中清空TxFIFO
*TXD0 to TXD1/TXD2/TXD3
*TXD1 to TXD0/TXD1/TXD2/TXD3
*TXD2 to TXD0/TXD1/TXD2/TXD3
*TXD3 to TXD0/TXD1/TXD2/TXD3
对于TXD0到TXD0访问,您不需要清空FIFO
在这里插入图片描述
FIFO读写
TxFIFO和RxFIFO共用同一个时钟。因此,对于每byte包括命令和地址数据从TxFIFO中移出,TxFIFO就会同时移入。
为了从QSPI读取数据,软件需要写对应的命令,地址、模式(当时Quad或者Dual I/O模式)以及QSPI flash存储器需要的虚拟间隔周期到TxFIFO中。另外,软件必须要写入另外的dummy数据,这些多余的dummy数据为了提供数据移位到RxFIFO所需要的时钟。
2.4 I/O模式考虑
TxFiFO中断状态寄存器在数据真正可读之前就只为置位。在读取这个状态寄存器的延时时间里,数据就变得正真可读。
在读命令中,软件必须往TxFIFO中写入dummy数据来从设备中读取数据,在自动模式下,如果TxFIFO为空,QSPI控制器就会失能CS。为了接受数据,软件必须发送读命令和地址数据给设备。
2.5,线性地址模式
线性地址模式自动实现QSPI命令转换。
在线性地址模式下,这个flash memoy子系统像一个自读存储器
2.6,不支持的设备
有些自定义的协议在这里没有被支持
2.7,支持的读写命令
Supports commands that transfers address one bit per rising edge of SCK and return data 1, 2, or 4 bits of data per rising edge of SCK. These commands are called Read or Fast Read for 1-bit data; Dual Output Read for 2-bit data, and Quad Output for 4-bit data.
Supports commands that transfer both address and data 2 or 4 bits per rising edge of SCK. These are called Dual I/O for 2-bit and Quad I/O for 4-bit.
在这里插入图片描述

不通的命令下控制器会自动的识别是dual spi还是quad spi。这以外的命令,控制器采用标准的spi时序。

qspi协议介绍

标准SPI
标准的spi有四线,clk,cs,miso,mosi,是一种通用的串行总线标准

dual spi
针对flash存储器,输入输出同时工作的全双工不太实用,所以针对flash controller的dual spi数据线的数量不变,但是可以同时输入输出2bit数据,MISO,MOSI即可以作为输入,也可以作为输出。加快了数据的传输。

quad spi
quad spi 协议是在spi的基础上把数据线由原来的2根扩展为四根,四根可以同时为输入或者是输出。一般都是针对quad spi flash来做的控制,加快了数据的传输

qspi flash 介绍

有各种命令,包括dual spi 、标准spi、quad spi通信方式,每个命令对应着一种时序,查看数据手册以及主控的数据手册,完成对应的控制

块设备驱动框架介绍

块设备概念:块设备是指只能以块为单位进行访问的设备,块的大小一般是512个字节的整数倍。常见的块设备包括flash,SD卡,光盘等

1,块设备驱动的系统架构

在这里插入图片描述

qspi flash驱动介绍

出现的问题记录

1. 系统无法软重启

a,发现无法软件重启,只是把启动用的flash挂到linux系统中,所以应该是flash导致的。把设备树中的flash去掉,就可以正常启动。接下来通过现象排查是死在那个地方
b,断电能够正常起来;在内核中加入打印信息,发现往软重启寄存器里写入了重启命令,猜测系统已经重启,但是没有启动是因为启动部分的问题。
c,直接外部硬件复位引脚复位也没有反应,说明系统是在启动的时候没有从flash读取到启动文件导致的。
d,猜测应该是flash没有处在一个初始化的状态,到时zynq没法正常的操作。
e,参考开发板,挂在flash能够正常启动,对比flash_probe函数,发现两个函数的区别,一个设置了4位地址模式,一个是三位地址模式,查看数据手册,flash默认是3位地址模式,zynq启动的时候也是以三位地址模式来读取flash。
f,修改驱动,设置读写模式为3位地址模式。

猜你喜欢

转载自blog.csdn.net/weixin_43369409/article/details/83001164