主要包括两个SPI设备,AT45DB321D和MCP2515,一个是串行的数据闪存,一个是可以总线设备芯片。前者对于我们来说非常重要,我们可以借助该设备对uboot的和内核以及根文件系统进行更新。
预备知识:设备和驱动是如何匹配的?系统的热插拔是如何实现的?
首先一点,设备和驱动是严格区分的,设备是设备,驱动是驱动,设备通过struct device来定义,当然用户也可以将该结构体封装到自定义的设备结构体中,例如,struct platform_device,这是我们采用platform_bus_type总线的设备定义的结构体形式:
预备知识:设备和驱动是如何匹配的?系统的热插拔是如何实现的?
首先一点,设备和驱动是严格区分的,设备是设备,驱动是驱动,设备通过struct device来定义,当然用户也可以将该结构体封装到自定义的设备结构体中,例如,struct platform_device,这是我们采用platform_bus_type总线的设备定义的结构体形式:
include / linux / platform_device.h文件中:
struct platform_device {
const char * name;
u32 id;
struct device dev;
u32 num_resources;
struct resource * resource;
};
只要是9260的外围模块,就像IIC硬件控制器,SPI硬件控制器,都被完全的定义成这种结构体的格式,这种结构体主要包含硬件资源和名称,硬件资源分为寄存器和IRQ两种.platform_device通过向内核注册struct device dev这个结构体来告诉内核加载这个设备,
方法就是device_register(&platform_device-> dev)
内核不关心你使用的是platform_device还是spi_device,内核只关心你的struct设备结构体,内核通过这个struct设备结构体自然能够顺利摸瓜找到你是platform_device还是spi_device,这就是linux最引以为傲的contian_of()大法。
struct platform_device {
const char * name;
u32 id;
struct device dev;
u32 num_resources;
struct resource * resource;
};
只要是9260的外围模块,就像IIC硬件控制器,SPI硬件控制器,都被完全的定义成这种结构体的格式,这种结构体主要包含硬件资源和名称,硬件资源分为寄存器和IRQ两种.platform_device通过向内核注册struct device dev这个结构体来告诉内核加载这个设备,
方法就是device_register(&platform_device-> dev)
内核不关心你使用的是platform_device还是spi_device,内核只关心你的struct设备结构体,内核通过这个struct设备结构体自然能够顺利摸瓜找到你是platform_device还是spi_device,这就是linux最引以为傲的contian_of()大法。
驱动通过struct driver这个结构体来定义,与struct device一致,你也可以用自己的结构体去封装:例如,struct platform_driver。include
/ linux / platform_device.h文件中:
/ linux / platform_device.h文件中:
struct platform_driver {
int(* probe)(struct platform_device *);
int(* remove)(struct platform_device *);
void(* shutdown)(struct platform_device *);
int(* suspend)(struct platform_device *,pm_message_t state);
int(* suspend_late)(struct platform_device *,pm_message_t state);
int(* resume_early)(struct platform_device *);
int(* resume)(struct platform_device *);
struct device_driver driver;
};
与设备一致,应用程序通过driver_register(&platform_driver-> driver)向内核中注册你当前的驱动,而内核不关心你封装成的结构,而内核搜索的方法还是同样的contain_of大法。
int(* probe)(struct platform_device *);
int(* remove)(struct platform_device *);
void(* shutdown)(struct platform_device *);
int(* suspend)(struct platform_device *,pm_message_t state);
int(* suspend_late)(struct platform_device *,pm_message_t state);
int(* resume_early)(struct platform_device *);
int(* resume)(struct platform_device *);
struct device_driver driver;
};
与设备一致,应用程序通过driver_register(&platform_driver-> driver)向内核中注册你当前的驱动,而内核不关心你封装成的结构,而内核搜索的方法还是同样的contain_of大法。
系统如何将这两个匹配上的?而不会将iic的设备加载到spi的驱动上面?不会将这个iic设备的驱动加载到那个iic设备上,设备和驱动之间是如何联系的?总线,这就是总线的作用!
include / linux / device.h文件中有总线类型的定义。struct
bus_type {
const char * name;
include / linux / device.h文件中有总线类型的定义。struct
bus_type {
const char * name;
结构子系统
struct kset驱动程序;
struct kset devices;
struct klist klist_devices;
struct klist klist_drivers;
struct kset驱动程序;
struct kset devices;
struct klist klist_devices;
struct klist klist_drivers;
struct blocking_notifier_head bus_notifier;
struct bus_attribute * bus_attrs;
struct device_attribute * dev_attrs;
struct driver_attribute * drv_attrs;
struct device_attribute * dev_attrs;
struct driver_attribute * drv_attrs;
int(* match)(struct device * dev,struct device_driver * drv);
int(* uevent)(struct device * dev,char ** envp,
int num_envp,char * buffer,int buffer_size);
int(* probe)(struct device * dev);
int(* remove)(struct device * dev);
void(* shutdown)(struct device * dev);
int(* uevent)(struct device * dev,char ** envp,
int num_envp,char * buffer,int buffer_size);
int(* probe)(struct device * dev);
int(* remove)(struct device * dev);
void(* shutdown)(struct device * dev);
int(* suspend)(struct device * dev,pm_message_t state);
int(* suspend_late)(struct device * dev,pm_message_t state);
int(* resume_early)(struct device * dev);
int(* resume)(struct device * dev);
};
这个总线设备中最重要的可能是匹配成员,由于我们一般很少去建立一个新的总线,所以我们很少涉及总线的编程,我们只关注我们所关注的。
总线如何将两者关联起来,热插拔大家知道吧,当一个设备被通过device_register注册到内核中时,会导致一个热插拔事件产生,系统会遍历该总线上的所有驱动程序,调用总线的匹配算法,来寻找与该设备相匹配的驱动程序,当一个驱动注册到内核的时候,处理过程与此相似(这是我理解的阿,大家批评指正),而一般的macth算法都比较简单,例如platform_bus的匹配算法很简单,就是比较platform_device和platform_driver的名称成员,如果匹配成功,就加载相应的设备或者驱动!这完成了一个连接的过程...
int(* suspend_late)(struct device * dev,pm_message_t state);
int(* resume_early)(struct device * dev);
int(* resume)(struct device * dev);
};
这个总线设备中最重要的可能是匹配成员,由于我们一般很少去建立一个新的总线,所以我们很少涉及总线的编程,我们只关注我们所关注的。
总线如何将两者关联起来,热插拔大家知道吧,当一个设备被通过device_register注册到内核中时,会导致一个热插拔事件产生,系统会遍历该总线上的所有驱动程序,调用总线的匹配算法,来寻找与该设备相匹配的驱动程序,当一个驱动注册到内核的时候,处理过程与此相似(这是我理解的阿,大家批评指正),而一般的macth算法都比较简单,例如platform_bus的匹配算法很简单,就是比较platform_device和platform_driver的名称成员,如果匹配成功,就加载相应的设备或者驱动!这完成了一个连接的过程...
那么这两种设备驱动中最重要的类型在linux中如何表现出来,那我们就必须介绍一下从2.6开始实现的sys文件系统了,
/ sys / bus $ cat / etc / fstab
proc / proc proc defaults 0 0
devpts / dev / pts devpts defaults 0 0
tmpfs / dev / shm tmpfs defaults 0 0
sysfs / sys sysfs defaults 0 0
/ dev / mtdblock2 / mnt / flash2 yaffs defaults 0 0
proc / proc proc defaults 0 0
devpts / dev / pts devpts defaults 0 0
tmpfs / dev / shm tmpfs defaults 0 0
sysfs / sys sysfs defaults 0 0
/ dev / mtdblock2 / mnt / flash2 yaffs defaults 0 0
加载这个文件系统对我们分析设备模型是非常有好处的
。sys文件夹下一般有如下的目录:
/ sys $ ls -al
drwxr-xr-x 10 root root 0 Jan 1 1970。
drwxrwxrwx 11 1000 tao 4096 May 22 06:56 ..
drwxr-xr-x 7 root root 0 Oct 27 14:09 block
drwxr-xr-x 8 root root 0 Jan 1 1970 bus
drwxr-xr-x 21 root root 0 Jan 1 1970 class
drwxr-xr-x 4 root root 0 Jan 1 1970 devices
drwxr-xr-x 2 root root 0 Jan 1 1970 firmware
drwxr-xr-x 2 root root 0 Jan 1 1970 fs
drwxr-xr-x 2 root root 0 Jan 1 1970 kernel
drwxr-xr-x 22 root root 0 Oct 27 14:10 module
block是由于历史原因形成的块设备的文件夹。
我们以spi设备为例:spi部分要包括两种设备,一种是platform_device,一种是spi_device。
在弓/ ARM /马赫-AT91 / at91sam9260_device.c文件中,定义的SPI硬件控制模块设备,这我们不需要关心。
还有一种是spi_device ,定义在arch / arm / mach-at91 / board-sam9260ek.c文件中,这就是我们的dataflash和mcp2515设备,
所以如下设置加载成功的话,在总线下面的每个目录里面,都存在设备和驱动程序两个文件夹,分别对应设备和文件。
/ SYS /巴士/平台/设备$ LS -al
drwxr-XR-X 2根根0 10月27日16:01。
drwxr-xr-x 4 root root 0 Jan 1 1970 ..
lrwxrwxrwx 1 root root 0 Oct 27 16:01 at91_i2c - > ../../../devices/platform/at91_i2c
lrwxrwxrwx 1 root root 0 Oct 27 16: 01 at91_nand - > ../../../devices/platform/at91_nand
lrwxrwxrwx 1 root root 0 Oct 27 16:01 at91_ohci - > ../../../devices/platform/at91_ohci
lrwxrwxrwx 1 root root 0 Oct 27 16:01 atmel_spi.0 - > ../../../devices/platform/atmel_spi.0
lrwxrwxrwx 1 root root 0 Oct 27 16:01 atmel_spi.1 - > ../../../ devices / platform / atmel_spi.1
lrwxrwxrwx 1 root root 0 Oct 27 16:01 atmel_usart.0 - > ../../../devices/platform/atmel_usart.0
lrwxrwxrwx 1 root root 0 Oct 27 16:01 atmel_usart。 1 - > ../../../devices/platform/atmel_usart.1
lrwxrwxrwx 1 root root 0 Oct 27 16:01 atmel_usart.2 - > ../../../devices/platform/atmel_usart.2
lrwxrwxrwx 1 root root 0 Oct 27 16:01 macb - > ../../../devices/platform/macb
驱动
/ sys / bus / platform / drivers / atmel_spi $ ls -al
drwxr-xr-x 2 root root 0 Jan 1 1970。
drwxr-xr-x 8 root root 0 Jan 1 1970 ..
lrwxrwxrwx 1 root root 0 Oct 27 16:10 atmel_spi.0 - > ../../../../devices/platform/atmel_spi.0
lrwxrwxrwx 1 root root 0 Oct 27 16:10 atmel_spi.1 - > ../../../../devices/platform/atmel_spi.1 --w
------- 1 root root 4096 Oct 27 16: 10 bind
--w ------- 1 root root 4096 Oct 27 16:10 unbind
如果出现上面的这个情况,说明你的设备(两路spi总线)和驱动都加载成功了,如果你的设备下面没有spi.0设备和spi.1设备的话,说明
board-sam9260ek.c文件中的这个函数出错:
static void __init ek_board_init(void)
{
/ * Serial * /
at91_add_device_serial();
/ * USB Host * /
at91_add_device_usbh(&ek_usbh_data);
/ * USB Device * /
at91_add_device_udc(&ek_udc_data);
/ * SPI * /
at91_add_device_spi(ek_spi_devices,ARRAY_SIZE(ek_spi_devices));
/ * NAND * /
at91_add_device_nand(&ek_nand_data);
/ * Ethernet * /
at91_add_device_eth(&ek_macb_data);
/ * MMC * /
at91_add_device_mmc(0,&ek_mmc_data);
/ * I2C * /
at91_add_device_i2c();
}
这里是设备注册的地方,我们还应该在下面这个目录下看到这两个文件。/
sys / bus / spi / devices $ ls -al
drwxr-xr-x 2 root root 0 Oct 27 14:09。
drwxr-xr-x 4 root root 0 Jan 1 1970 ..
lrwxrwxrwx 1 root root 0 Oct 27 14:09 spi0.1 - > ../../../devices/platform/atmel_spi.0/spi0.1
lrwxrwxrwx 1 root root 0 Oct 27 14:09 spi1.0 - > ../../../devices/platform/atmel_spi.1/spi1.0
这两个链接说明我们的两个spi设备注册都被接受了,有人看不懂这个系统文件系统的层次关系,其实这里比较好说明,就是spi0.1是atmel_spi.0设备的子设备嘛,很好理解的。
。sys文件夹下一般有如下的目录:
/ sys $ ls -al
drwxr-xr-x 10 root root 0 Jan 1 1970。
drwxrwxrwx 11 1000 tao 4096 May 22 06:56 ..
drwxr-xr-x 7 root root 0 Oct 27 14:09 block
drwxr-xr-x 8 root root 0 Jan 1 1970 bus
drwxr-xr-x 21 root root 0 Jan 1 1970 class
drwxr-xr-x 4 root root 0 Jan 1 1970 devices
drwxr-xr-x 2 root root 0 Jan 1 1970 firmware
drwxr-xr-x 2 root root 0 Jan 1 1970 fs
drwxr-xr-x 2 root root 0 Jan 1 1970 kernel
drwxr-xr-x 22 root root 0 Oct 27 14:10 module
block是由于历史原因形成的块设备的文件夹。
我们以spi设备为例:spi部分要包括两种设备,一种是platform_device,一种是spi_device。
在弓/ ARM /马赫-AT91 / at91sam9260_device.c文件中,定义的SPI硬件控制模块设备,这我们不需要关心。
还有一种是spi_device ,定义在arch / arm / mach-at91 / board-sam9260ek.c文件中,这就是我们的dataflash和mcp2515设备,
所以如下设置加载成功的话,在总线下面的每个目录里面,都存在设备和驱动程序两个文件夹,分别对应设备和文件。
/ SYS /巴士/平台/设备$ LS -al
drwxr-XR-X 2根根0 10月27日16:01。
drwxr-xr-x 4 root root 0 Jan 1 1970 ..
lrwxrwxrwx 1 root root 0 Oct 27 16:01 at91_i2c - > ../../../devices/platform/at91_i2c
lrwxrwxrwx 1 root root 0 Oct 27 16: 01 at91_nand - > ../../../devices/platform/at91_nand
lrwxrwxrwx 1 root root 0 Oct 27 16:01 at91_ohci - > ../../../devices/platform/at91_ohci
lrwxrwxrwx 1 root root 0 Oct 27 16:01 atmel_spi.0 - > ../../../devices/platform/atmel_spi.0
lrwxrwxrwx 1 root root 0 Oct 27 16:01 atmel_spi.1 - > ../../../ devices / platform / atmel_spi.1
lrwxrwxrwx 1 root root 0 Oct 27 16:01 atmel_usart.0 - > ../../../devices/platform/atmel_usart.0
lrwxrwxrwx 1 root root 0 Oct 27 16:01 atmel_usart。 1 - > ../../../devices/platform/atmel_usart.1
lrwxrwxrwx 1 root root 0 Oct 27 16:01 atmel_usart.2 - > ../../../devices/platform/atmel_usart.2
lrwxrwxrwx 1 root root 0 Oct 27 16:01 macb - > ../../../devices/platform/macb
驱动
/ sys / bus / platform / drivers / atmel_spi $ ls -al
drwxr-xr-x 2 root root 0 Jan 1 1970。
drwxr-xr-x 8 root root 0 Jan 1 1970 ..
lrwxrwxrwx 1 root root 0 Oct 27 16:10 atmel_spi.0 - > ../../../../devices/platform/atmel_spi.0
lrwxrwxrwx 1 root root 0 Oct 27 16:10 atmel_spi.1 - > ../../../../devices/platform/atmel_spi.1 --w
------- 1 root root 4096 Oct 27 16: 10 bind
--w ------- 1 root root 4096 Oct 27 16:10 unbind
如果出现上面的这个情况,说明你的设备(两路spi总线)和驱动都加载成功了,如果你的设备下面没有spi.0设备和spi.1设备的话,说明
board-sam9260ek.c文件中的这个函数出错:
static void __init ek_board_init(void)
{
/ * Serial * /
at91_add_device_serial();
/ * USB Host * /
at91_add_device_usbh(&ek_usbh_data);
/ * USB Device * /
at91_add_device_udc(&ek_udc_data);
/ * SPI * /
at91_add_device_spi(ek_spi_devices,ARRAY_SIZE(ek_spi_devices));
/ * NAND * /
at91_add_device_nand(&ek_nand_data);
/ * Ethernet * /
at91_add_device_eth(&ek_macb_data);
/ * MMC * /
at91_add_device_mmc(0,&ek_mmc_data);
/ * I2C * /
at91_add_device_i2c();
}
这里是设备注册的地方,我们还应该在下面这个目录下看到这两个文件。/
sys / bus / spi / devices $ ls -al
drwxr-xr-x 2 root root 0 Oct 27 14:09。
drwxr-xr-x 4 root root 0 Jan 1 1970 ..
lrwxrwxrwx 1 root root 0 Oct 27 14:09 spi0.1 - > ../../../devices/platform/atmel_spi.0/spi0.1
lrwxrwxrwx 1 root root 0 Oct 27 14:09 spi1.0 - > ../../../devices/platform/atmel_spi.1/spi1.0
这两个链接说明我们的两个spi设备注册都被接受了,有人看不懂这个系统文件系统的层次关系,其实这里比较好说明,就是spi0.1是atmel_spi.0设备的子设备嘛,很好理解的。
驱动:
platform_driver驱动:
/ sys / bus / platform / drivers $ ls -al
drwxr-xr-x 8 root root 0 Jan 1 1970。
drwxr-xr-x 4 root root 0 Jan 1 1970 ..
drwxr-xr-x 2 root root 0 Jan 1 1970 at91_i2c
drwxr-xr-x 2 root root 0 Jan 1 1970 at91_nand
drwxr-xr-x 2 root root 0 Jan 1 1970 at91_ohci
drwxr-xr-x 2 root root 0 Oct 27 16:10 atmel_spi
drwxr-xr-x 2 root root 0 Jan 1 1970 atmel_usart
drwxr-xr-x 2 root root 0 Jan 1 1970 macb
我们可以看到这个驱动只有一个atmel_spi,这个驱动是在哪个加载的?
driver / spi / atmel_spi.c文件加载的。
platform_driver驱动:
/ sys / bus / platform / drivers $ ls -al
drwxr-xr-x 8 root root 0 Jan 1 1970。
drwxr-xr-x 4 root root 0 Jan 1 1970 ..
drwxr-xr-x 2 root root 0 Jan 1 1970 at91_i2c
drwxr-xr-x 2 root root 0 Jan 1 1970 at91_nand
drwxr-xr-x 2 root root 0 Jan 1 1970 at91_ohci
drwxr-xr-x 2 root root 0 Oct 27 16:10 atmel_spi
drwxr-xr-x 2 root root 0 Jan 1 1970 atmel_usart
drwxr-xr-x 2 root root 0 Jan 1 1970 macb
我们可以看到这个驱动只有一个atmel_spi,这个驱动是在哪个加载的?
driver / spi / atmel_spi.c文件加载的。
spi_driver驱动:
/ sys / bus / spi / drivers $ ls -al
drwxr-xr-x 4 root root 0 Oct 27 14:10。
drwxr-xr-x 4 root root 0 Jan 1 1970 ..
drwxr-xr-x 2 root root 0 Oct 27 14:10 mcp2515
drwxr-xr-x 2 root root 0 Oct 27 14:09 mtd_dataflash
这是我们加载的两个驱动,说明驱动也加载正常了。
/ sys / bus / spi / drivers $ ls -al
drwxr-xr-x 4 root root 0 Oct 27 14:10。
drwxr-xr-x 4 root root 0 Jan 1 1970 ..
drwxr-xr-x 2 root root 0 Oct 27 14:10 mcp2515
drwxr-xr-x 2 root root 0 Oct 27 14:09 mtd_dataflash
这是我们加载的两个驱动,说明驱动也加载正常了。
下面我们来说说我们遇到的问题吧。
在设备和驱动都加载正常之后,出现与dataflash设备通信不上的情况,驱动加载的时候,读取芯片的状态字读出是0xff,说明工作不正常,动用逻辑分析仪监控spi总线的通信,意外的发现,sq信号和cs信号正常,但是mosi无信号输出,开始觉得可能是spi总线适配器有问题,后来仔细观察原理图之后,发现dataflash和mmc / sd是使用同样的io口的,即pa0,pa1,pa2,而我的内核配置中打开了对mmc的支持,所以导致mosi不正常,所以可能9260的mmc与dataflash不能同时使用,但9263的可以。
解决办法:make menuconfig
设备驱动---> MMC / SD卡支持,取消其支持,问题解决!
在设备和驱动都加载正常之后,出现与dataflash设备通信不上的情况,驱动加载的时候,读取芯片的状态字读出是0xff,说明工作不正常,动用逻辑分析仪监控spi总线的通信,意外的发现,sq信号和cs信号正常,但是mosi无信号输出,开始觉得可能是spi总线适配器有问题,后来仔细观察原理图之后,发现dataflash和mmc / sd是使用同样的io口的,即pa0,pa1,pa2,而我的内核配置中打开了对mmc的支持,所以导致mosi不正常,所以可能9260的mmc与dataflash不能同时使用,但9263的可以。
解决办法:make menuconfig
设备驱动---> MMC / SD卡支持,取消其支持,问题解决!
还有一个问题可能大家没有注意到,没有解释清楚,其实是有问题的,我们的at91_add_device_spi函数如下:
static struct spi_board_info ek_spi_devices [] = {
#if!defined(CONFIG_MMC_AT91)
{/ * DataFlash chip * /
.modalias =“mtd_dataflash”,.
chip_select = 1,
.max_speed_hz = 15 * 1000 * 1000,
bus_num = 0
,
#如果定义(CONFIG_MTD_AT91_DATAFLASH_CARD)
{/ *数据闪存卡* /
.modalias =“mtd_dataflash”,
.chip_select = 0,
.max_speed_hz = 15 * 1000 * 1000,
.bus_num = 0,
},
#ENDIF
#ENDIF
#如果定义(CONFIG_SND_AT73C213 )|| 定义(CONFIG_SND_AT73C213_MODULE)
{/ * AT73C213 DAC * /
.modalias =“AT73C213”,
.chip_select = 0,
.max_speed_hz = 10 * 1000 * 1000,
.bus_num = 1,
},
的#endif
#if!defined(CONFIG_MMC_AT91)
{/ * DataFlash chip * /
.modalias =“mtd_dataflash”,.
chip_select = 1,
.max_speed_hz = 15 * 1000 * 1000,
bus_num = 0
,
#如果定义(CONFIG_MTD_AT91_DATAFLASH_CARD)
{/ *数据闪存卡* /
.modalias =“mtd_dataflash”,
.chip_select = 0,
.max_speed_hz = 15 * 1000 * 1000,
.bus_num = 0,
},
#ENDIF
#ENDIF
#如果定义(CONFIG_SND_AT73C213 )|| 定义(CONFIG_SND_AT73C213_MODULE)
{/ * AT73C213 DAC * /
.modalias =“AT73C213”,
.chip_select = 0,
.max_speed_hz = 10 * 1000 * 1000,
.bus_num = 1,
},
的#endif
/ * spi可以通过mrz添加* /
#if定义(CONFIG_CAN_MCP2515_MODULE)||定义(CONFIG_CAN_MCP2515)
//定义(CONFIG_CAN_MCP2515)
{.
modalias =“
mcp2515 ”,. chip_select = 0,
// .controller_data = AT91_PIN_PB3,
.irq = AT91_PIN_PC6,// AT91SAM9260_ID_IRQ0,
.platform_data = mcp251x_data,
.max_speed_hz = 10 * 1000 * 1000,
.bus_num = 1,
.mode = 0,
},
/ *
{
.modalias =“MCP2515”,
.chip_select = 1,
// .controller_data = AT91_PIN_PC5,
.irq = AT91_PIN_PC7,// AT91SAM9260_ID_IRQ1,
.platform_data =&mcp251x_data,
.max_speed_hz = 10 * 1000 * 1000,
.bus_num = 1,
.mode = 0,
},
* /
#elif defined(CONFIG_CAN_MCP251X)
{
。 modalias =“mcp251x”,.
chip_select = 0,
// .controller_data = AT91_PIN_PB3,
.irq = AT91_PIN_PC6,// AT91SAM9260_ID_IRQ0,
.platform_data =&mcp251x_data,
.max_speed_hz = 10 * 1000 * 1000,
bus_num = 1,
.mode = 0 ,
},
{.
modalias =“mcp251x”,.
chip_select = 1,
// .controller_data = AT91_PIN_PC5,
.irq = AT91_PIN_PC7,// AT91SAM9260_ID_IRQ1,
.platform_data =&mcp251x_data,
.max_speed_hz = 10 * 1000 * 1000,
bus_num = 1 ,
.mode = 0,
},
#ENDIF
}
无效__init at91_add_device_spi(结构spi_board_info *设备,诠释nr_devices)
{
诠释我;
unsigned long cs_pin;
short enable_spi0 = 0;
short enable_spi1 = 0;
#if定义(CONFIG_CAN_MCP2515_MODULE)||定义(CONFIG_CAN_MCP2515)
//定义(CONFIG_CAN_MCP2515)
{.
modalias =“
mcp2515 ”,. chip_select = 0,
// .controller_data = AT91_PIN_PB3,
.irq = AT91_PIN_PC6,// AT91SAM9260_ID_IRQ0,
.platform_data = mcp251x_data,
.max_speed_hz = 10 * 1000 * 1000,
.bus_num = 1,
.mode = 0,
},
/ *
{
.modalias =“MCP2515”,
.chip_select = 1,
// .controller_data = AT91_PIN_PC5,
.irq = AT91_PIN_PC7,// AT91SAM9260_ID_IRQ1,
.platform_data =&mcp251x_data,
.max_speed_hz = 10 * 1000 * 1000,
.bus_num = 1,
.mode = 0,
},
* /
#elif defined(CONFIG_CAN_MCP251X)
{
。 modalias =“mcp251x”,.
chip_select = 0,
// .controller_data = AT91_PIN_PB3,
.irq = AT91_PIN_PC6,// AT91SAM9260_ID_IRQ0,
.platform_data =&mcp251x_data,
.max_speed_hz = 10 * 1000 * 1000,
bus_num = 1,
.mode = 0 ,
},
{.
modalias =“mcp251x”,.
chip_select = 1,
// .controller_data = AT91_PIN_PC5,
.irq = AT91_PIN_PC7,// AT91SAM9260_ID_IRQ1,
.platform_data =&mcp251x_data,
.max_speed_hz = 10 * 1000 * 1000,
bus_num = 1 ,
.mode = 0,
},
#ENDIF
}
无效__init at91_add_device_spi(结构spi_board_info *设备,诠释nr_devices)
{
诠释我;
unsigned long cs_pin;
short enable_spi0 = 0;
short enable_spi1 = 0;
/ *选择SPI芯片选择* /
/ *这里加载我们定义的spi_board_info结构体,也就是两个spi设备的信息,注意,他们这里没有使用spi_device结构体来做,而是使用一个板级信息体完成* /
for(i = 0; i <nr_devices; i ++){
/ *该成员定义的就是cs引脚* /
if(devices [i] .controller_data)
cs_pin =(unsigned long)devices [i] .controller_data ;
else if(devices [i] .bus_num == 0)
cs_pin = spi0_standard_cs [devices [i] .chip_select];
else
cs_pin = spi1_standard_cs [devices [i] .chip_select];
/ *根据需要加载的设备,确定需要打开哪几个SPI控制器,我们系统中有两个控制器,所以我们在以模块的方式加载驱动的时候,我们的设备必须在刚开始就被初始化! * /
if(devices [i] .bus_num == 0)
enable_spi0 = 1;
else
enable_spi1 = 1;
/ *这里加载我们定义的spi_board_info结构体,也就是两个spi设备的信息,注意,他们这里没有使用spi_device结构体来做,而是使用一个板级信息体完成* /
for(i = 0; i <nr_devices; i ++){
/ *该成员定义的就是cs引脚* /
if(devices [i] .controller_data)
cs_pin =(unsigned long)devices [i] .controller_data ;
else if(devices [i] .bus_num == 0)
cs_pin = spi0_standard_cs [devices [i] .chip_select];
else
cs_pin = spi1_standard_cs [devices [i] .chip_select];
/ *根据需要加载的设备,确定需要打开哪几个SPI控制器,我们系统中有两个控制器,所以我们在以模块的方式加载驱动的时候,我们的设备必须在刚开始就被初始化! * /
if(devices [i] .bus_num == 0)
enable_spi0 = 1;
else
enable_spi1 = 1;
/ *使能芯片选择引脚* /
/ *将片选引脚设置为输出* /
at91_set_gpio_output(cs_pin,1);
/ *将片选引脚设置为输出* /
at91_set_gpio_output(cs_pin,1);
/ *通过芯片选择引脚到驱动程序* /
devices [i] .controller_data =(void *)cs_pin;
}
/ *到此,循环执行完毕,向内核注册这些板级信息体* /
spi_register_board_info(devices,nr_devices);
devices [i] .controller_data =(void *)cs_pin;
}
/ *到此,循环执行完毕,向内核注册这些板级信息体* /
spi_register_board_info(devices,nr_devices);
/ *配置SPI总线* /
/ *如果发现spi0上有设备注册,则打开
spi0 * / if(enable_spi0){
at91_set_A_periph(AT91_PIN_PA0,0); / * SPI0_MISO * /
at91_set_A_periph(AT91_PIN_PA1,0); / * SPI0_MOSI * /
at91_set_A_periph(AT91_PIN_PA2,0); / * SPI1_SPCK * /
/ *如果发现spi0上有设备注册,则打开
spi0 * / if(enable_spi0){
at91_set_A_periph(AT91_PIN_PA0,0); / * SPI0_MISO * /
at91_set_A_periph(AT91_PIN_PA1,0); / * SPI0_MOSI * /
at91_set_A_periph(AT91_PIN_PA2,0); / * SPI1_SPCK * /
at91_clock_associate(“spi0_clk”,&at91sam9260_spi0_device.dev,“spi_clk”);
platform_device_register(&at91sam9260_spi0_device);
}
/ * spi0设备也是如此* /
if(enable_spi1){
at91_set_A_periph(AT91_PIN_PB0,0); / * SPI1_MISO * /
at91_set_A_periph(AT91_PIN_PB1,0); / * SPI1_MOSI * /
at91_set_A_periph(AT91_PIN_PB2,0); / * SPI1_SPCK * /
platform_device_register(&at91sam9260_spi0_device);
}
/ * spi0设备也是如此* /
if(enable_spi1){
at91_set_A_periph(AT91_PIN_PB0,0); / * SPI1_MISO * /
at91_set_A_periph(AT91_PIN_PB1,0); / * SPI1_MOSI * /
at91_set_A_periph(AT91_PIN_PB2,0); / * SPI1_SPCK * /
at91_clock_associate(“spi1_clk”,&at91sam9260_spi1_device.dev,“spi_clk”);
platform_device_register(&at91sam9260_spi1_device);
}
}
platform_device_register(&at91sam9260_spi1_device);
}
}
从上面这个函数我们可以看出,这个函数就完成了两个功能:
1,向内核完成spi板级信息结构体的注册
2,注册了两个platform_device:spi0与spi1,这两个设备是spi总线控制器!
1,向内核完成spi板级信息结构体的注册
2,注册了两个platform_device:spi0与spi1,这两个设备是spi总线控制器!
那么我们客户端spi_device设备的注册是如完成的?不知道,呵呵
我今天仔细的看代码才发现玄机所在。
内核的注释很清晰的告诉我们,我们的spi设备是不允许热插拔!!是由于spi设备驱动的框架不允许,我们的spi_device 设备注册不是在板级初始化的时候完成的,而是在spi控制器的驱动加载的时候,也就是platform_driver:atmel_spi驱动加载的时候完成。
我今天仔细的看代码才发现玄机所在。
内核的注释很清晰的告诉我们,我们的spi设备是不允许热插拔!!是由于spi设备驱动的框架不允许,我们的spi_device 设备注册不是在板级初始化的时候完成的,而是在spi控制器的驱动加载的时候,也就是platform_driver:atmel_spi驱动加载的时候完成。
(atmel9260驱动文件atmel_spi.c)
driver / spi / atmel_spi.c文件中:
static int __init atmel_spi_probe(struct platform_device * pdev)
{
struct resource * regs;
int irq;
struct clk * clk;
int ret;
struct spi_master * master;
struct atmel_spi * as;
driver / spi / atmel_spi.c文件中:
static int __init atmel_spi_probe(struct platform_device * pdev)
{
struct resource * regs;
int irq;
struct clk * clk;
int ret;
struct spi_master * master;
struct atmel_spi * as;
regs = platform_get_resource(pdev,IORESOURCE_MEM,0);
if(!regs)
return -ENXIO;
if(!regs)
return -ENXIO;
irq = platform_get_irq(pdev,0);
if(irq <0)
return irq;
if(irq <0)
return irq;
clk = clk_get(&pdev-> dev,“spi_clk”);
如果(IS_ERR(clk))
返回PTR_ERR(clk);
如果(IS_ERR(clk))
返回PTR_ERR(clk);
/ * setup spi core then atmel-specific driver state * /
ret = -ENOMEM;
master = spi_alloc_master(&pdev-> dev,sizeof * as);
if(!master)
goto out_free;
ret = -ENOMEM;
master = spi_alloc_master(&pdev-> dev,sizeof * as);
if(!master)
goto out_free;
master-> bus_num = pdev-> id;
master-> num_chipselect = 4;
master-> setup = atmel_spi_setup;
master-> transfer = atmel_spi_transfer;
master-> cleanup = atmel_spi_cleanup;
platform_set_drvdata(pdev,master);
master-> num_chipselect = 4;
master-> setup = atmel_spi_setup;
master-> transfer = atmel_spi_transfer;
master-> cleanup = atmel_spi_cleanup;
platform_set_drvdata(pdev,master);
as = spi_master_get_devdata(master);
as-> buffer = dma_alloc_coherent(&pdev-> dev,BUFFER_SIZE,
&as-> buffer_dma,GFP_KERNEL);
if(!as-> buffer)
goto out_free;
&as-> buffer_dma,GFP_KERNEL);
if(!as-> buffer)
goto out_free;
spin_lock_init(&as-> lock);
INIT_LIST_HEAD(&as-> queue);
as-> pdev = pdev;
as-> regs = ioremap(regs-> start,(regs-> end-regs-> start)+1);
if(!as-> regs)
goto out_free_buffer;
as-> irq = irq;
as-> clk = clk;
#ifdef CONFIG_ARCH_AT91
if(!cpu_is_at91rm9200())
as-> new_1 = 1;
#万一
INIT_LIST_HEAD(&as-> queue);
as-> pdev = pdev;
as-> regs = ioremap(regs-> start,(regs-> end-regs-> start)+1);
if(!as-> regs)
goto out_free_buffer;
as-> irq = irq;
as-> clk = clk;
#ifdef CONFIG_ARCH_AT91
if(!cpu_is_at91rm9200())
as-> new_1 = 1;
#万一
ret = request_irq(irq,atmel_spi_interrupt,0,
pdev-> dev.bus_id,master);
if(ret)
goto out_unmap_regs;
pdev-> dev.bus_id,master);
if(ret)
goto out_unmap_regs;
/ *初始化硬件* /
clk_enable(clk);
spi_writel(as,CR,SPI_BIT(SWRST));
spi_writel(as,MR,SPI_BIT(MSTR)| SPI_BIT(MODFDIS));
spi_writel(as,PTCR,SPI_BIT(RXTDIS)| SPI_BIT(TXTDIS));
spi_writel(as,CR,SPI_BIT(SPIEN));
clk_enable(clk);
spi_writel(as,CR,SPI_BIT(SWRST));
spi_writel(as,MR,SPI_BIT(MSTR)| SPI_BIT(MODFDIS));
spi_writel(as,PTCR,SPI_BIT(RXTDIS)| SPI_BIT(TXTDIS));
spi_writel(as,CR,SPI_BIT(SPIEN));
/* 走!* /
dev_info(&pdev-> dev,“Atmel SPI Controller at 0x%08lx(irq%d)\ n”,
(unsigned long)regs-> start,irq);
/ * spi注册这个主控制器* /
ret = spi_register_master(master);
if(ret)
goto out_reset_hw;
dev_info(&pdev-> dev,“Atmel SPI Controller at 0x%08lx(irq%d)\ n”,
(unsigned long)regs-> start,irq);
/ * spi注册这个主控制器* /
ret = spi_register_master(master);
if(ret)
goto out_reset_hw;
return 0;
out_reset_hw:
spi_writel(as,CR,SPI_BIT(SWRST));
clk_disable(clk);
free_irq(irq,master);
out_unmap_regs:
iounmap(as-> regs);
out_free_buffer:
dma_free_coherent(&pdev-> dev,BUFFER_SIZE,as-> buffer,
as-> buffer_dma);
out_free:
clk_put(clk);
spi_master_put(master);
return ret;
}
而这个spi_register_master位于driver / spi / spi.c文件中,该函数调用了scan_boardinfo(master),该函数就存在于该文件下:该函数调用了spi_new_device(master,chip) ,这个芯片就是一个spi_board_info结构体,这就是at91_add_device_spi第一个作用的用处:向内核的链表注册spi_board_info结构体的用处所在。我们来看函数的调用过程:
atmel_spi_probe -----> spi_register_master --- - > scan_boardinfo
----> spi_new_device
我们来看这个spi_new_device函数:
struct spi_device * spi_new_device(struct spi_master * master,
struct spi_board_info * chip)
{
struct spi_device * proxy;
struct device * dev = master-> cdev.dev;
int status;
spi_writel(as,CR,SPI_BIT(SWRST));
clk_disable(clk);
free_irq(irq,master);
out_unmap_regs:
iounmap(as-> regs);
out_free_buffer:
dma_free_coherent(&pdev-> dev,BUFFER_SIZE,as-> buffer,
as-> buffer_dma);
out_free:
clk_put(clk);
spi_master_put(master);
return ret;
}
而这个spi_register_master位于driver / spi / spi.c文件中,该函数调用了scan_boardinfo(master),该函数就存在于该文件下:该函数调用了spi_new_device(master,chip) ,这个芯片就是一个spi_board_info结构体,这就是at91_add_device_spi第一个作用的用处:向内核的链表注册spi_board_info结构体的用处所在。我们来看函数的调用过程:
atmel_spi_probe -----> spi_register_master --- - > scan_boardinfo
----> spi_new_device
我们来看这个spi_new_device函数:
struct spi_device * spi_new_device(struct spi_master * master,
struct spi_board_info * chip)
{
struct spi_device * proxy;
struct device * dev = master-> cdev.dev;
int status;
/ *注意:调用者做了任何chip-> bus_num检查* /
if(!spi_master_get(master))
return NULL;
/ *靠,终于找到你了,先暴打一顿,舒服了..这里就分配了我们重要的spi_device结构体* /
proxy = kzalloc(sizeof * proxy,GFP_KERNEL);
if(!proxy){
dev_err(dev,“can not alloc dev for cs%d \ n”,
chip-> chip_select);
goto失败;
}
/ *这就是将我们的信息体中的数据转化为spi_device识别的数据* /
proxy-> master = master;
proxy-> chip_select = chip-> chip_select;
proxy-> max_speed_hz = chip-> max_speed_hz;
proxy-> mode = chip-> mode;
proxy-> irq = chip-> irq;
proxy-> modalias = chip-> modalias;
return NULL;
/ *靠,终于找到你了,先暴打一顿,舒服了..这里就分配了我们重要的spi_device结构体* /
proxy = kzalloc(sizeof * proxy,GFP_KERNEL);
if(!proxy){
dev_err(dev,“can not alloc dev for cs%d \ n”,
chip-> chip_select);
goto失败;
}
/ *这就是将我们的信息体中的数据转化为spi_device识别的数据* /
proxy-> master = master;
proxy-> chip_select = chip-> chip_select;
proxy-> max_speed_hz = chip-> max_speed_hz;
proxy-> mode = chip-> mode;
proxy-> irq = chip-> irq;
proxy-> modalias = chip-> modalias;
snprintf(proxy-> dev.bus_id,sizeof proxy-> dev.bus_id,
“%s。%u”,master-> cdev.class_id,
chip-> chip_select);
proxy-> dev.parent = dev;
proxy-> dev.bus =&spi_bus_type;
/ *这里很重要,如果你的spi设备是dataflash的话,保存的就是你的分区表!!!所以我们要返回去修改我们的spi_boardinfo结构体* /
proxy-> dev.platform_data =(void *)chip - > platform_data;
/ *片选信号* /
proxy-> controller_data = chip-> controller_data;
proxy-> controller_state = NULL;
proxy-> dev.release = spidev_release;
“%s。%u”,master-> cdev.class_id,
chip-> chip_select);
proxy-> dev.parent = dev;
proxy-> dev.bus =&spi_bus_type;
/ *这里很重要,如果你的spi设备是dataflash的话,保存的就是你的分区表!!!所以我们要返回去修改我们的spi_boardinfo结构体* /
proxy-> dev.platform_data =(void *)chip - > platform_data;
/ *片选信号* /
proxy-> controller_data = chip-> controller_data;
proxy-> controller_state = NULL;
proxy-> dev.release = spidev_release;
/ *驱动程序可以修改此默认i / o设置* /
status = master-> setup(proxy);
if(status <0){
dev_dbg(dev,“can not%s%s,status%d \ n”,
“setup”,proxy-> dev.bus_id,status);
goto失败;
}}
status = master-> setup(proxy);
if(status <0){
dev_dbg(dev,“can not%s%s,status%d \ n”,
“setup”,proxy-> dev.bus_id,status);
goto失败;
}}
/ *驱动程序核心通过定义
已经存在的*设备来捕获行为不端的调用者。
* /
/ *看到这句话,大家放心了吧,大家也就知道怎么找到spi_driver驱动的... * /
status = device_register(&proxy-> dev);
if(status <0){
dev_dbg(dev,“can not%s%s,status%d \ n”,
“add”,proxy-> dev.bus_id,status);
goto失败;
}
dev_dbg(dev,“registered child%s \ n”,proxy-> dev.bus_id);
返回代理
已经存在的*设备来捕获行为不端的调用者。
* /
/ *看到这句话,大家放心了吧,大家也就知道怎么找到spi_driver驱动的... * /
status = device_register(&proxy-> dev);
if(status <0){
dev_dbg(dev,“can not%s%s,status%d \ n”,
“add”,proxy-> dev.bus_id,status);
goto失败;
}
dev_dbg(dev,“registered child%s \ n”,proxy-> dev.bus_id);
返回代理
fail:
spi_master_put(master);
kfree(proxy);
返回NULL;
}}
spi_master_put(master);
kfree(proxy);
返回NULL;
}}
下面我们要解决最后的一个问题,dataflash的分区的问题,看了这么多,大家应该知道怎么解决了吧!
我们看mtd_dataflash.c文件中驱动加载函数调用下面这个函数来添加flash设备
static int __devinit
add_dataflash(struct spi_device * spi,char * name,
int nr_pages,int pagesize,int pageoffset)
{
struct dataflash * priv;
struct mtd_info * device;
/ *这里就告诉我们要在spi_boardinfo结构体的platform_data成员指向一个我们需要的flash_platform_data数据!* /
struct flash_platform_data * pdata = spi-> dev.platform_data;
add_dataflash(struct spi_device * spi,char * name,
int nr_pages,int pagesize,int pageoffset)
{
struct dataflash * priv;
struct mtd_info * device;
/ *这里就告诉我们要在spi_boardinfo结构体的platform_data成员指向一个我们需要的flash_platform_data数据!* /
struct flash_platform_data * pdata = spi-> dev.platform_data;
priv = kzalloc(sizeof * priv,GFP_KERNEL);
if(!priv)
return -ENOMEM;
if(!priv)
return -ENOMEM;
init_MUTEX(&priv-> lock);
priv-> spi = spi;
priv-> page_size = pagesize;
priv-> page_offset = pageoffset;
priv-> spi = spi;
priv-> page_size = pagesize;
priv-> page_offset = pageoffset;
/ * name必须可用于cmdlinepart * /
sprintf(priv-> name,“spi%d。%d-%s”,
spi-> master-> bus_num,spi-> chip_select,
name);
sprintf(priv-> name,“spi%d。%d-%s”,
spi-> master-> bus_num,spi-> chip_select,
name);
device =&priv-> mtd;
device-> name =(pdata && pdata-> name)?pdata-> name:priv-> name;
device-> size = nr_pages * pagesize;
device-> erasesize = pagesize;
device-> writesize = pagesize;
device-> owner = THIS_MODULE;
device-> type = MTD_DATAFLASH;
device-> flags = MTD_WRITEABLE;
device-> erase = dataflash_erase;
device-> read = dataflash_read;
device-> write = dataflash_write;
device-> priv = priv;
device-> name =(pdata && pdata-> name)?pdata-> name:priv-> name;
device-> size = nr_pages * pagesize;
device-> erasesize = pagesize;
device-> writesize = pagesize;
device-> owner = THIS_MODULE;
device-> type = MTD_DATAFLASH;
device-> flags = MTD_WRITEABLE;
device-> erase = dataflash_erase;
device-> read = dataflash_read;
device-> write = dataflash_write;
device-> priv = priv;
dev_info(&spi-> dev,“%s(%d KBytes)\ n”,name,device-> size / 1024);
dev_set_drvdata(&spi-> dev,priv);
dev_set_drvdata(&spi-> dev,priv);
if(mtd_has_partitions()){
struct mtd_partition * parts;
int nr_parts = 0;
/ *我们这里没有定义该宏,所以不会在命令行传递分区表* /
#ifdef CONFIG_MTD_CMDLINE_PARTS
static const char * part_probes [] = {“cmdlinepart”,NULL,};
struct mtd_partition * parts;
int nr_parts = 0;
/ *我们这里没有定义该宏,所以不会在命令行传递分区表* /
#ifdef CONFIG_MTD_CMDLINE_PARTS
static const char * part_probes [] = {“cmdlinepart”,NULL,};
nr_parts = parse_mtd_partitions(device,part_probes,&parts,0);
#万一
#万一
if(nr_parts <= 0 && pdata && pdata-> parts){
parts = pdata-> parts;
nr_parts = pdata-> nr_parts;
}}
parts = pdata-> parts;
nr_parts = pdata-> nr_parts;
}}
if(nr_parts> 0){
priv-> partitioning = 1;
return add_mtd_partitions(device,parts,nr_parts);
}
} else if(pdata && pdata-> nr_parts)
dev_warn(&spi-> dev,“忽略%s上的%d个默认分区\ n”,
pdata-> nr_parts,device-> name);
priv-> partitioning = 1;
return add_mtd_partitions(device,parts,nr_parts);
}
} else if(pdata && pdata-> nr_parts)
dev_warn(&spi-> dev,“忽略%s上的%d个默认分区\ n”,
pdata-> nr_parts,device-> name);
return add_mtd_device(device)== 1?-ENODEV:0;
}}
}}
所以我们需要修改这个文件:
arch / arm / mach-at91 / board-sam9260ek.c文件:
添加如下:
arch / arm / mach-at91 / board-sam9260ek.c文件:
添加如下:
#if!defined(CONFIG_MMC_AT91)
#define SIZE_1PAGE 528
#define SIZE_1M(unsigned long)(1024 * 1024)
static struct mtd_partition ek_dataflash_partition [] = {
{
.name =“U-boot ENV”,.
offset = 0,
.size = 64 * SIZE_1PAGE,
},
{.name
=“U-BOOT”,.
offset = 64 * SIZE_1PAGE,
.size = 400 * SIZE_1PAGE,
},
{.name
=“Kernel”,.
offset = 464 * SIZE_1PAGE,
.size = 4000 * SIZE_1PAGE,
},
{.name
=“Root fs”,.
offset = 4464 * SIZE_1PAGE,
.size =(8192-4464)* SIZE_1PAGE,
},
};
#define SIZE_1PAGE 528
#define SIZE_1M(unsigned long)(1024 * 1024)
static struct mtd_partition ek_dataflash_partition [] = {
{
.name =“U-boot ENV”,.
offset = 0,
.size = 64 * SIZE_1PAGE,
},
{.name
=“U-BOOT”,.
offset = 64 * SIZE_1PAGE,
.size = 400 * SIZE_1PAGE,
},
{.name
=“Kernel”,.
offset = 464 * SIZE_1PAGE,
.size = 4000 * SIZE_1PAGE,
},
{.name
=“Root fs”,.
offset = 4464 * SIZE_1PAGE,
.size =(8192-4464)* SIZE_1PAGE,
},
};
struct flash_platform_data dataflash_atmel = {
.name =“AT45DB321”,.
parts = ek_dataflash_partition,
.nr_parts = ARRAY_SIZE(ek_dataflash_partition),
};
#万一
修改spi_boardinfo结构体:
static struct spi_board_info ek_spi_devices [] = {
#if!defined(CONFIG_MMC_AT91)
{/ * DataFlash chip * /
.modalias =“mtd_dataflash”,.
chip_select = 1,
.max_speed_hz = 15 * 1000 * 1000,
.bus_num = 0,
.platform_data =&dataflash_atmel,
},
添加platform_data结构成员。
这里我们建立mtd_partition结构体要注意,由于dataflash是以528字节每页的,其实,at45db321x芯片可以设置为512字节每页,这个操作是不可以逆转的,那个位置是一个otp位,的人就应该知道,但是出厂的时候默认的528字节每页。
如果我们不是以528个字节为单位的话,内核将出警告,强制分区加载为readonly格式。
到这,分区加载成功, dmesg输出如下信息:
<6> mtd_dataflash spi0.1:AT45DB321x(4224 KBytes)
<5>在“AT45DB321”上创建4个MTD分区:
<5> 0x00000000-0x00008400:“U-boot ENV”
<5> 0x00008400-0x0003bd00:“U-BOOT”
<5> 0x0003bd00-0x0023f700:“Kernel”
<5> 0x0023f700-0x00420000:“Root fs”
<5>在“AT45DB321”上创建4个MTD分区:
<5> 0x00000000-0x00008400:“U-boot ENV”
<5> 0x00008400-0x0003bd00:“U-BOOT”
<5> 0x0003bd00-0x0023f700:“Kernel”
<5> 0x0023f700-0x00420000:“Root fs”
linux简直太伟大了,使用越越多,就越能体会到其思想的伟大