SPI设备的驱动
主要包括两个SPI设备,at45db321d和mcp2515,一个是串行的dataflash,一个是can总
线设备芯片。前者对于我们来说非常重要,我们可以借助该设备对uboot和kernel以及
根文件系统进行更新。
预备知识:设备和驱动是如何匹配的?系统的热插拔是如何实现的?
首先一点,设备和驱动是严格区分的,设备是设备,驱动是驱动,设备通过struct
device来定义,当然用户也可以将该结构体封装到自己定义的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
device结构体,内核通过这个struct device结构体自然能够顺藤摸瓜找到你是
platform_device还是spi_device,这就是linux最引以为傲的contian_of()大法。
驱动通过struct driver这个结构体来定义,与struct device一致,你也可以用自己的
结构体去封装:例如,struct platform_driver。
include/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;
};
与device一致,应用程序通过driver_register(&platform_driver->driver)向内核中
注册你当前的驱动,而内核不关心你封装成的结构,而内核搜索的方法还是同样的
contain_of大法。
系统如何将这两者匹配上的?而不会将iic的设备加载到spi的驱动上面?不会将这个
iic设备的驱动加载到那个iic设备上,设备和驱动之间是如何联系的?总线,这就是总
线的作用!
include/linux/device.h文件中有总线类型的定义。
struct bus_type {
const char * name;
struct subsystem subsys;
struct kset drivers;
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;
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 (*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);
};
这个总线设备中最重要的可能是match成员,由于我们一般很少去建立一个新的总线,
所以我们很少涉及总线的编程,我们就只关注我们所关注的。
总线如何将两者关联起来,热插拔大家知道吧,当一个设备被通过device_register注
册到内核中时,会导致一个热插拔事件产生,系统会遍历该总线上的所有驱动程序,调
用bus的match算法,来寻找与该设备相匹配的驱动程序,当一个驱动注册到内核的时
候,处理过程与此相似(这是我理解的阿,大家批评指正),而一般的macth算法都比
较简单,例如platform_bus的匹配算法就很简单,就是比较platform_device和
platform_driver的name成员,如果匹配成功,就加载相应的设备或者驱动!这就完成
了一个连接的过程。。。
那么这两种设备驱动中最重要的类型在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
加载这个文件系统对于我们分析设备模型是非常有好处的。
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是由于历史原因形成的block设备的文件夹。我们关心的是bus文件夹。
我们以spi设备为例:spi部分要包括两种设备,一种是platform_device,一种是
spi_device。
在arch/arm/mach-at91/at91sam9260_device.c文件中,定义的SPI硬件控制模块设备,
这我们不需要关心。
还有一种是spi_device,定义在arch/arm/mach-at91/board-sam9260ek.c文件中,这就
是我们的dataflash和mcp2515设备,
所以如何设备加载成功的话,在bus下面的每个目录里面,都存在devices和drivers两
个文件夹,分别对应设备和文件。
/sys/bus/platform/devices $ ls -al
drwxr-xr-x 2 root root 0 Oct 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总线)和驱动都加载成功了,如果
你的devices下面没有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设备注册都被接受了,剩下来就是驱动的问题。有人看
不懂这个sys文件系统的层次关系,其实这里比较好说明,就是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文件加载的。
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
这是我们加载的两个驱动,说明驱动也加载正常了。
下面我们来说说我们遇到的问题吧。
在设备和驱动都加载正常之后,出现与dataflash设备通信不上的情况,驱动加载的时
候,读取芯片的状态字读出是0xff,说明工作不正常,动用逻辑分析仪监控spi总线的
通信,意外的发现,sck信号和cs信号正常,但是mosi无信号输出,开始觉得可能是spi
总线适配器有问题,后来仔细观察原理图之后,发现dataflash和mmc/sd是使用同样的
io口的,即pa0,pa1,pa2,而我的内核配置中打开了对mmc的支持,所以导致mosi不正
常,所以可能9260的mmc与dataflash不能同时使用,但9263的可以。
解决办法:make menuconfig
Device Drivers--->MMC/SD card support,取消其支持,问题解决!
还有一个问题可能大家没有注意到,没有解释清楚,其实是有问题的,我们的
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,
},
#if defined(CONFIG_MTD_AT91_DATAFLASH_CARD)
{ /* DataFlash card */
.modalias = "mtd_dataflash",
.chip_select = 0,
.max_speed_hz = 15 * 1000 * 1000,
.bus_num = 0,
},
#endif
#endif
#if defined(CONFIG_SND_AT73C213) || defined(CONFIG_SND_AT73C213_MODULE)
{ /* AT73C213 DAC */
.modalias = "at73c213",
.chip_select = 0,
.max_speed_hz = 10 * 1000 * 1000,
.bus_num = 1,
},
#endif
/* spi can ,add by mrz */
#if defined(CONFIG_CAN_MCP2515_MODULE) ||defined(CONFIG_CAN_MCP2515)
//defined(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
}
void __init at91_add_device_spi(struct spi_board_info *devices, int
nr_devices)
{
int i;
unsigned long cs_pin;
short enable_spi0 = 0;
short enable_spi1 = 0;
/* Choose SPI chip-selects */
/*这里加载我们定义的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;
/* enable chip-select pin */
/*将片选引脚设置为输出*/
at91_set_gpio_output(cs_pin, 1);
/* pass chip-select pin to driver */
devices[i].controller_data = (void *) cs_pin;
}
/*到此,循环执行完毕,向内核注册这些板级信息体*/
spi_register_board_info(devices, nr_devices);
/* Configure SPI bus(es) */
/*如果发现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 */
at91_clock_associate("spi1_clk", &at91sam9260_spi1_device.dev,
"spi_clk");
platform_device_register(&at91sam9260_spi1_device);
}
}
从上面这个函数我们可以看出,这个函数就完成了两个功能:
1、向内核完成spi板级信息结构体的注册
2、注册了两个platform_device:spi0与spi1,这两个设备是spi总线控制器!
那么我们客户端spi_device设备的注册是如何完成的?不知道,呵呵
我今天仔细的看代码才发现玄机所在。
内核的注释很清晰的告诉我们,我们的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;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!regs)
return -ENXIO;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
clk = clk_get(&pdev->dev, "spi_clk");
if (IS_ERR(clk))
return 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;
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);
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;
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;
#endif
ret = request_irq(irq, atmel_spi_interrupt, 0,
pdev->dev.bus_id, master);
if (ret)
goto out_unmap_regs;
/* Initialize the hardware */
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));
/* go! */
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 master下面设备。该函数就存在于该文件下:该
函数调用了spi_new_device(master, chip),这个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;
/* NOTE: caller did any chip->bus_num checks necessary */
if (!spi_master_get(master))
return NULL;
/*靠,终于找到你了,先暴打一顿,舒服了。。这里就分配了我们重要的
spi_device结构体*/
proxy = kzalloc(sizeof *proxy, GFP_KERNEL);
if (!proxy) {
dev_err(dev, "can't alloc dev for cs%d/n",
chip->chip_select);
goto fail;
}
/*这就是将我们的信息体中的数据转化为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;
/* drivers may modify this default i/o setup */
status = master->setup(proxy);
if (status < 0) {
dev_dbg(dev, "can't %s %s, status %d/n",
"setup", proxy->dev.bus_id, status);
goto fail;
}
/* driver core catches callers that misbehave by defining
* devices that already exist.
*/
/*看到这句话,大家放心了吧,大家也就知道怎么找到spi_driver驱动的。。。*/
status = device_register(&proxy->dev);
if (status < 0) {
dev_dbg(dev, "can't %s %s, status %d/n",
"add", proxy->dev.bus_id, status);
goto fail;
}
dev_dbg(dev, "registered child %s/n", proxy->dev.bus_id);
return proxy;
fail:
spi_master_put(master);
kfree(proxy);
return 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;
priv = kzalloc(sizeof *priv, GFP_KERNEL);
if (!priv)
return -ENOMEM;
init_MUTEX(&priv->lock);
priv->spi = spi;
priv->page_size = pagesize;
priv->page_offset = pageoffset;
/* name must be usable with cmdlinepart */
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;
dev_info(&spi->dev, "%s (%d KBytes)/n", name, device->size/1024);
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, };
nr_parts = parse_mtd_partitions(device, part_probes, &parts, 0);
#endif
if (nr_parts <= 0 && pdata && pdata->parts) {
parts = pdata->parts;
nr_parts = pdata->nr_parts;
}
if (nr_parts > 0) {
priv->partitioned = 1;
return add_mtd_partitions(device, parts, nr_parts);
}
} else if (pdata && pdata->nr_parts)
dev_warn(&spi->dev, "ignoring %d default partitions on %s/n",
pdata->nr_parts, device->name);
return add_mtd_device(device) == 1 ? -ENODEV : 0;
}
所以我们需要修改这个文件:
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,
},
};
struct flash_platform_data dataflash_atmel={
.name="AT45DB321",
.parts=ek_dataflash_partition,
.nr_parts=ARRAY_SIZE(ek_dataflash_partition),
};
#endif
修改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>Creating 4 MTD partitions on "AT45DB321":
<5>0x00000000-0x00008400 : "U-boot ENV"
<5>0x00008400-0x0003bd00 : "U-BOOT"
<5>0x0003bd00-0x0023f700 : "Kernel"
<5>0x0023f700-0x00420000 : "Root fs"
linux简直太伟大了,使用得越多,就越能体会到其思想的伟大
文章出处:
http://www.diybl.com/course/6_system/linux/Linuxjs/200868/123621_3.html
相关推荐
Linux spi设备驱动
Linux SPI驱动分主机驱动和设备驱动,主机驱动往往由厂商实现好,第三方开发员直接开发设备驱动即可。下面不阐述SPI子系统及驱动相关的架构,直接上一个SPI接口的温度传感器(TMP125)驱动来理解SPI驱动如何运作。这里也涉及Linux 驱动模型的知识。 在板级文件增加SPI设备信息: static struct omap2_mcspi_device_config ads784
SPI设备的驱动-1[转]
今天折腾了一天的SPI设备的驱动加载,甚至动用了逻辑分析仪来查看spi总线的波形,主要包括两个SPI设备,at45db321d和mcp2515,一个是串行的dataflash,一个是can总线设备芯片。前者对于我们来说非常重要,我们可以借助该设备对uboot和kernel以及根文件系统进行更新。 预备知识:设备和驱动是如何匹配的?系统的热插拔是如何实现的? 首先一点,设备和驱动是严格区
AM335X——SPI设备驱动
优快云仅用于增加百度收录权重,排版未优化,日常不维护。请访问:www.hceng.cn 查看、评论。 本博文对应地址: https://hceng.cn/2019/01/01/AM335X——SPI设备驱动/#more 最近在AM335X上写了几个SPI设备驱动,记录一下心得。 1. 准备工作 与前面写过的I2C驱动一样,SPI驱动也涉及SPI控制器(适配器)驱动和SPI设备驱动。 SPI控制...
【转载】AT45D081/AT45DB021/AT45DB0xx驱动程序(C语言)用于大型存储的串行Flash
AT45D081/AT45DB021/AT45DB0xx驱动程序(C语言)用于大型存储的串行Flash /*---------------------------------------------------------版 本: V2.2作 者:梁衍龙创作时间:2002年9月==========================================================*
adesto-AT45DB321E:一个使AT45DB321E闪存芯片正常工作的简单库
adesto-AT45DB321E 一个使AT45DB321E闪存芯片正常工作的简单库 兼容ANSI-C 对于调试模式,您需要存储库。 由def定义的WAsys_adesto_AT45DB321E_flash_spi_DEBUG编译,或者可以在头文件中显式定义。 用法 初始化(定义)实例WAsys_FLASH_CONFIG_T类型。 实现CS(片选),io读/写功能 通过将以上内容传递给WAsys_flash_init函数来初始化库
AT91与MCP2515实现CAN(中断方式自收发)
AT91与MCP2515实现CAN(中断方式自收发)
SPI驱动学习五(如何编写SPI设备驱动程序)
SPI Master(或者说控制器) 通过platform总线设备驱动模型进行实现,SPI Device通过SPI driver驱动模型来实现。SPI设备的设备树节点,会被转换为一个spi_device结构体。在SPI子系统中,用spi_transfer结构体描述一个传输,用spi_message管理多个传输。DAC模块接在这个插座上,那么要在设备树里spi1的节点下创建子节点。在这个节点下,创建子节点,用来表示SPI设备。将该部分内容添加到主控板的设备树文件中。在设备树里,找到SPI控制器的节点。
单细胞分析进阶:sctransform标准化在Seurat中的实战解析
本文深入解析了sctransform标准化方法在Seurat单细胞分析中的实战应用。通过对比传统对数标准化,sctransform能更有效地建模并剔除技术噪音,提升数据质量。文章提供了从环境配置、数据准备、核心参数解析到完整流程整合的详细指南,并针对常见问题给出解决方案,帮助研究者获得更清晰的细胞分群和更可靠的差异表达结果。
SPI驱动之SPI设备驱动程序
前篇文章分析了SPI主控制器驱动,也就是SPI总线驱动,既然总线有了,根据Linux设备驱动模型,还得有SPI设备和SPI设备驱动。SPI设备是在板级文件中注册,SPI设备驱动需要用户自己实现,好在内核为我们提供了一个通用的SPI设备驱动spidev.c,下面就来分析一下这个文件,该文件位于kernel3.0.15/drivers/spi/spidev.c。 1. 模块初始化和注销:sp
ROS2_Moveit2_Ur5e_Grasp项目详解(二):核心模块功能解析
本文深入解析了ROS2_Moveit2_Ur5e_Grasp项目的核心模块功能。重点阐述了OctoMap建图模块如何为机械臂提供实时三维环境感知,并详细介绍了Robotiq夹爪与MoveIt2的集成控制、UR5e的运动规划策略,以及视觉模块如何协同工作,最终实现从目标检测到动态抓取的完整闭环。
4、设备树的处理(1)
其实到第4节才写设备树的问题完全是因为忘记了,之前有一个版本是先关闭了设备树的,习惯性的认为设备树已经处理完了,失误了!!!前文说过,这个系统是依着荣品的开发板和核心板来的,使用的是5.1内核的SDK。它的SDK里是开发板全功能的,所以设备树也是的。
SPI设备驱动
申请或者 定义一个spi_driver,然后初始化spi_driver中的各个成员变量,当SPI设备和驱动匹配以后,spi_driver下的probe函数就会执行!构建spi_transfer,然后将其打包到spi_message里面,需要使用spi_message_init初始化spi_message,然后在使用spi_message_add_tail将spi_transfer添加到spi_message里面,最终使用spi_sync和spi_async来发送。:与SPI设备对应的SPI驱动。
刷题单向链表
实现一个数据结构,维护一张表(最初只有一个元素 11)。接下来 qq 行,每行表示一次操作,操作具体间题目描述。a[i] 里面放的是下一个元素,为0代表链表结束。如单链表1->4 -> 2->3->0用数组表示是。对于每个操作 2,输出一个数字,用换行隔开。第一行一个整数 qq 表示操作次数。
LINUX SPI设备驱动模型分析之一 SPI设备驱动框架介绍
从本章开始我们将进行spi驱动模块的分析,spi驱动模块与i2c驱动模块的实现非常相似。在spi驱动模块中主要涉及spi总线、spi master、spi device、 spi driver、spi通信接口、spi master字符设备几个部分。 spi驱动模块主要涉及如下几个内容: spi驱动模块框架说明 spi总线部分分析 spi设备注册与注销部分分析 spi驱动注册与注...
从零到一:手把手教你用海康VisionMaster完成第一个字符识别项目(附完整流程与避坑点)
本文详细介绍了如何使用海康VisionMaster完成首个字符识别项目,涵盖从环境搭建、图像预处理到系统集成的完整流程。特别分享了Blob分析配置、字符识别优化等实战技巧,帮助开发者避开常见陷阱,快速掌握VisionMaster的基本操作,提升工业自动化场景下的字符识别效率。
从加权最小二乘(WLS)到IAA:手把手推导DOA估计中的迭代自适应核心公式
本文深入解析了从加权最小二乘(WLS)到迭代自适应算法(IAA)在DOA估计中的数学推导与应用。通过动态调整加权矩阵,IAA在低快拍数、相干源等复杂场景中展现出卓越性能,显著提升角度分辨率和检测概率。文章详细介绍了算法核心步骤、工程优化策略及前沿发展,为雷达探测和无线通信领域提供重要参考。
Linux设备驱动之SPI驱动
不同点在于,不同的SoC的SPI控制器的寄存器不同,因此SPI的速度和模式等配置方式不同。主机驱动要干的事就是:申请(Linux提供API)一个spi_master结构体,然后按照本SoC硬件的实际情况去填充结构体成员,特别是把用于通信的函数写好,最后向系统注册这个SPI控制器。描述,该结构体中包含了SPI控制器的序号(很多SoC中存在多个SPI控制器),片选数量,SPI信息传输的速率,配置SPI模式的函数指针(4种模式),实现数据传输的函数指针。Linux下SPI驱动分成两部分:主机驱动和设备驱动。
PHM2010刀具磨损预测完整实现
本文介绍了PHM2010刀具磨损预测项目的完整实现方案。项目采用PHM2010公开数据集,包含6个子数据集,记录了铣削加工过程中7个通道的传感器数据(切削力、振动和声发射信号)。技术路线包括数据预处理(截取中间10万点、小波降噪)、特征提取(时域9个、频域4个特征)、特征融合和皮尔逊相关系数筛选,最终使用BiLSTM模型进行训练和评估。环境配置基于Python 3.9,依赖NumPy、Pandas、PyTorch等库。数据加载和预处理阶段重点处理了传感器信号分段和磨损标签对齐问题,为后续建模奠定基础。
Linux下SPI设备驱动开发
引脚含义: DO(MOSI):Master Output, Slave Input,SPI主控用来发出数据,SPI从设备用来接收数据。 DI(MISO):Master Input, Slave Output,SPI主控用来发出数据,SPI从设备用来接收数据。 SCK:Serial Clock,时钟。 CS:Chip Select,芯片选择引脚。 设现在主控芯片要传输一个0x56数据给SPI FLASH,时序如下: 首先我们要
X86/64架构中的总线 和 ARM架构中总线的理解
内部总线 /CPU总线/FSB前端总线:类似ARM架构中的AHB总线北桥? 直接和CPU连接,这条总线主要由CPU使用,用来与高速缓存、主存和北桥(或MCH)之间传送信息。 外部总线:类似ARM架构中的APB总线(外围总线)南桥?例如PCI总线、USB总线等 通过北桥与CPU连接(南桥?),是CPU与内存RAM、ROM和输入/输出设备接口之间进行通讯的通路 内存总线:内存与北桥间的总线称为内存总线 ...
精雕软件(JDpaint) 5.5 绿色版
精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版
Linux设备驱动开发-SPI驱动开发详解(包含设备树处理详细过程)
引脚:MISO(master 输入,slave 输出),MOSI(master 输出,slave 输入),片选引脚,SCK(时钟)控制寄存器:可以设置这CPOL 和 CPHA两个参数,CPOL 代表 SCK 初始电平,CPHA 代表相位(第一/第二个时钟沿采集数据),SPI 状态寄存器:分辨数据是否发送完了,使能中断波特率寄存器:设置 SCK 频率数据寄存器:连接移位器收发数据。
Statistics and Machine Learning Toolbox
Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具
驱动程序开发:SPI设备驱动
使用SPI框架和字符设备框架读取SPI设备(icm20608传感器),思路清晰!
文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况
主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。
计算机控制技术及应用
《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。
嵌入式Linux SPI驱动 编写SPI设备驱动程序 spi_transfer spi_message
嵌入式Linux SPI驱动 编写SPI设备驱动程序 详细解析
2061



被折叠的 条评论
为什么被折叠?



