nuc972一个spi口接两片flash
nuc972的spi支持两个从设备选择线,挂载两个spi flash后可以实现自动切换,不需要手动的拉低片选,功能已经在源码中做好了,需要我们在make menuconfig中配置好,还要在arch/arm/mach-nuc970/dev.c中添加设备数据,这样就支持两片flash正常工作了。最后添加Quad模式。具体操作如下
在文件drivers/spi/spi-nuc970-p0.c中控制spi从设备的代码部分如下
static inline void nuc970_slave_select(struct spi_device *spi, unsigned int ssr)
{
struct nuc970_spi *hw = (struct nuc970_spi *)to_hw(spi);
unsigned int val;
unsigned int cs = spi->mode & SPI_CS_HIGH ? 1 : 0;
unsigned long flags;
spin_lock_irqsave(&hw->lock, flags);
val = __raw_readl(hw->regs + REG_SSR);
if (!cs)
val &= ~SELECTLEV;
else
val |= SELECTLEV;
if(spi->chip_select == 0) {
if (!ssr)
val &= ~SELECTSLAVE0;
else
val |= SELECTSLAVE0;
} else {
if (!ssr)
val &= ~SELECTSLAVE1;
else
val |= SELECTSLAVE1;
}
__raw_writel(val, hw->regs + REG_SSR);
spin_unlock_irqrestore(&hw->lock, flags);
}
static inline void nuc970_spi0_chipsel(struct spi_device *spi, int value)
{
switch (value) {
case BITBANG_CS_INACTIVE:
nuc970_slave_select(spi, 0);
break;
case BITBANG_CS_ACTIVE:
nuc970_slave_select(spi, 1);
break;
}
}
通过向SSR寄存器写入数据选择使用哪个spi flash,nuc970_spi0_chipsel函数控制slave device失能还是使能。nuc970_slave_select函数控制选择哪个设备,具体由spi->chip_select 的值决定。
在arch/arm/mach-nuc970/dev.c中添加设备信息
#if defined(CONFIG_SPI_NUC970_P0) || defined(CONFIG_SPI_NUC970_P0_MODULE)
/* spi device, spi flash info */
#ifdef CONFIG_MTD_M25P80
static struct mtd_partition nuc970_spi0_flash_partitions[] = {
#ifdef CONFIG_BOARD_ETH2UART
{
.name = "kernel",
.size = 0x0800000,
.offset = 0x1000000,
},
{
.name = "rootfs",
.size = 0x0800000,
.offset = 0x1800000,
},
#else
/* {
.name = "kernel",
.size = 0x0400000,
.offset = 0x200000,
},
{
.name = "rootfs",
.size = 0x0200000,
.offset = 0x0600000,
},*/
{
.name = "data",
.size = 0x1000000,
.offset = 0x0F00000,
},
#endif
};
static struct mtd_partition nuc970_spi0_1_flash_partitions[] = {
#ifdef CONFIG_BOARD_ETH2UART
{
.name = "kernel",
.size = 0x0800000,
.offset = 0x1000000,
},
{
.name = "rootfs",
.size = 0x0800000,
.offset = 0x1800000,
},
#else
{
.name = "app_data",
.size = 0x2000000,
.offset = 0,
},
#endif
};
static struct flash_platform_data nuc970_spi0_flash_data = {
/*#if 0
.name = "m25p80",
.parts = nuc970_spi0_flash_partitions,
.nr_parts = ARRAY_SIZE(nuc970_spi0_flash_partitions),
.type = "w25q128",
#endif*/
#if 1
.name = "m25p80",
.parts = nuc970_spi0_flash_partitions,
.nr_parts = ARRAY_SIZE(nuc970_spi0_flash_partitions),
.type = "gd25q256d",
#endif
};
static struct flash_platform_data nuc970_spi0_1_flash_data = {
.name = "m25p80",
.parts = nuc970_spi0_1_flash_partitions,
.nr_parts = ARRAY_SIZE(nuc970_spi0_1_flash_partitions),
.type = "gd25q256d",
};
#endif
static struct spi_board_info nuc970_spi0_board_info[] __initdata = {
#ifdef CONFIG_MTD_M25P80
{
.modalias = "m25p80",
.max_speed_hz = 15000000,
.bus_num = 0,
.chip_select = 0, //use SS0
.platform_data = &nuc970_spi0_flash_data,
#if defined(CONFIG_SPI_NUC970_P0_NORMAL)
.mode = (SPI_MODE_0 | SPI_RX_DUAL | SPI_TX_DUAL),
#elif defined(CONFIG_SPI_NUC970_P0_QUAD)
.mode = (SPI_MODE_0 | SPI_TX_QUAD | SPI_RX_QUAD),
#endif
},
{
.modalias = "m25p80",
.max_speed_hz = 15000000,
.bus_num = 0,
.chip_select = 1, //use SS0
.platform_data = &nuc970_spi0_1_flash_data,
#if defined(CONFIG_SPI_NUC970_P0_NORMAL)
.mode = (SPI_MODE_0 | SPI_RX_DUAL | SPI_TX_DUAL),
#elif defined(CONFIG_SPI_NUC970_P0_QUAD)
.mode = (SPI_MODE_0 | SPI_TX_QUAD | SPI_RX_QUAD),
#endif
},
#endif
#ifdef CONFIG_SPI_SPIDEV
{
.modalias = "spidev",
.max_speed_hz = 75000000,
.bus_num = 0,
.chip_select = 1, //use SS1
.mode = SPI_MODE_0,
},
#endif
};
static struct nuc970_spi_info nuc970_spi0_platform_data = {
.num_cs = 2,
.lsb = 0,
.txneg = 1,
.rxneg = 0,
.divider = 4,
.sleep = 0,
.txnum = 0,
.txbitlen = 8,
.bus_num = 0,
};
static struct resource nuc970_spi0_resource[] = {
[0] = {
.start = NUC970_PA_SPI0,
.end = NUC970_PA_SPI0 + NUC970_SZ_SPI0 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_SPI0,
.end = IRQ_SPI0,
.flags = IORESOURCE_IRQ,
}
};
nuc970_spi0_1_flash_partitions、nuc970_spi0_1_flash_data这两部分和下面这部分是我自己的添加的(就是把以前存在的复制出来修改修改就可以了)
{
.modalias = "m25p80",
.max_speed_hz = 15000000,
.bus_num = 0,
.chip_select = 1, //use SS0
.platform_data = &nuc970_spi0_1_flash_data,
#if defined(CONFIG_SPI_NUC970_P0_NORMAL)
.mode = (SPI_MODE_0 | SPI_RX_DUAL | SPI_TX_DUAL),
#elif defined(CONFIG_SPI_NUC970_P0_QUAD)
.mode = (SPI_MODE_0 | SPI_TX_QUAD | SPI_RX_QUAD),
#endif
}
到这基本就完成了,这次踩了个坑,开始把spi驱动编译成模块的时候,第二片选引脚的不能使能,导致第二片选一直是高电平,后来尝试了一下编译进内核,既然就可以了。在程序中也就是要执行这个p = devm_pinctrl_get_select(&pdev->dev, “spi0-ss1-PH”)才可以。此次使用的是PH12。
#if defined(CONFIG_OF)
p = devm_pinctrl_get_select_default(&pdev->dev);
#else
#if defined(CONFIG_SPI_NUC970_P0_NORMAL) && !defined(CONFIG_SPI_NUC970_P0_SS1)
p = devm_pinctrl_get_select(&pdev->dev, "spi0");
#elif defined(CONFIG_SPI_NUC970_P0_NORMAL) && defined(CONFIG_SPI_NUC970_P0_SS1_PB0)
p = devm_pinctrl_get_select(&pdev->dev, "spi0-ss1-PB");
#elif defined(CONFIG_SPI_NUC970_P0_NORMAL) && defined(CONFIG_SPI_NUC970_P0_SS1_PH12)
p = devm_pinctrl_get_select(&pdev->dev, "spi0-ss1-PH");
#elif defined(CONFIG_SPI_NUC970_P0_QUAD) && !defined(CONFIG_SPI_NUC970_P0_SS1)
p = devm_pinctrl_get_select(&pdev->dev, "spi0-quad");
#elif defined(CONFIG_SPI_NUC970_P0_QUAD) && defined(CONFIG_SPI_NUC970_P0_SS1_PB0)
p = devm_pinctrl_get_select(&pdev->dev, "spi0-quad-ss1-PB");
#elif defined(CONFIG_SPI_NUC970_P0_QUAD) && defined(CONFIG_SPI_NUC970_P0_SS1_PH12)
p = devm_pinctrl_get_select(&pdev->dev, "spi0-quad-ss1-PH");
#endif
#endif
编译烧录进内核就可以看到 多了“Creating MTD partitions on “m25p80””,加粗部分是添加后打印的。
nuc970-spi0 nuc970-spi0: master is unqueued, this is deprecated
m25p80: Enable Quad Mode
m25p80 spi0.0: gd25q256d (32768 Kbytes)
Creating 1 MTD partitions on “m25p80”:
0x000001000000-0x000002000000 : “app_data”
m25p80: Enable Quad Mode
m25p80 spi0.1: gd25q256d (32768 Kbytes)
Creating 1 MTD partitions on “m25p80”:
0x000000000000-0x000002000000 : "log_data"
spi-flash驱动还是用到了这些文件drivers/mtd/devices/m25p80.c,这个主要是添加自己所使用flash的信息。
完成上面的工作之后,开机可以在/dev/下能看到mtdblock的块分区,但是挂载时提示无效的参数,虽然看到设备节点了但还没有正确的驱动Flash。做如下修改,在drivers/mtd/devices/m25p80.c的set_quad_mode和set_4byte中添加GD25Q256D的代码
完整的set_4byte(gagiDevice部分为添加内容,CFI_MFR_GD定义在include\linux\mtd\cfi.h)
static inline int set_4byte(struct m25p *flash, u32 jedec_id, int enable)
{
switch (JEDEC_MFR(jedec_id)) {
case CFI_MFR_MACRONIX:
case 0xEF /* winbond */:
case CFI_MFR_EON: /* cFeon */
case CFI_MFR_GD:/*Gagi device*/
flash->command[0] = enable ? OPCODE_EN4B : OPCODE_EX4B;
return spi_write(flash->spi, flash->command, 1);
default:
/* Spansion style */
flash->command[0] = OPCODE_BRWR;
flash->command[1] = enable << 7;
return spi_write(flash->spi, flash->command, 2);
}
}
完整的set_quad_mode
static int set_quad_mode(struct m25p *flash, u32 jedec_id)
{
int status;
switch (JEDEC_MFR(jedec_id)) {
case CFI_MFR_GD: /*Gagidevice*/
status = gigadevice_quad_enable(flash);
if(status) {
dev_err(&flash->spi->dev,"giga quad-read not enabled\n");
return -EINVAL;
}
case 0xEF: /* Winbond */
status = winbond_quad_enable(flash);
if (status) {
dev_err(&flash->spi->dev,
"Winbond quad-read not enabled\n");
return -EINVAL;
}
default:
status = winbond_quad_enable(flash);
if (status) {
dev_err(&flash->spi->dev,
"Winbond quad-read not enabled\n");
return -EINVAL;
}
return status;
}
}
上面代码使用到了gigadevice_quad_enable,复制winbond_quad_enable的内容到gigadevice_quad_enable如下:
static int gigadevice_quad_enable(struct m25p *flash)
{
int ret, val;
u8 cmd[3];
u8 code;
/* write enable for volatile status register */
code = OPCODE_WRSR_VOL;
spi_write(flash->spi,&code ,1);
/* write status register */
cmd[0] = OPCODE_RDSR2;
val = read_sr2_winbond(flash);
cmd[1] = val | SR_QUAD_EN_WB;
spi_write(flash->spi, &cmd, 2);
if(wait_till_ready(flash))
return 1;
ret = read_sr2_winbond(flash);
if(!(ret > 0 &&(ret & SR_QUAD_EN_WB))) {
dev_err(&flash->spi->dev,"gagidevice Quad bit not set\n");
return -EINVAL;
}
write_disable(flash);
return 0;
}
重新烧录挂载,成功。