开发板:tiny4412SDK + S702 + 4GB Flash
要移植的内核版本:Linux-4.4.0 (支持device tree)
u-boot版本:友善之臂自带的 U-Boot 2010.12
busybox版本:busybox 1.25
目标:
驱动外接的8M的 spi flash,注册为块设备。
设备树:
&spi_0 {
status = "okay";
cs-gpios = <&gpb 1 GPIO_ACTIVE_HIGH>;
spi_flash@0 {
compatible = "tiny4412,spi_flash";
spi-max-frequency = <10000000>;
reg = <0>;
controller-data {
samsung,spi-feedback-delay = <0>;
};
};
};
这里指定的 reg = <0> 表示的该 spi 设备引用第一个 cs-gpios
spi-max-frequency = <10000000>;表示最大速率
代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* 参考:
* drivers\mtd\devices\mtdram.c
* drivers/mtd/devices/m25p80.c
*/
static struct spi_device *spi_flash;
void SPIFlashReadID(int *pMID, int *pDID)
{
unsigned char tx_buf[4];
unsigned char rx_buf[2];
//printk("%s\n",__func__);
tx_buf[0] = 0x90;
tx_buf[1] = 0;
tx_buf[2] = 0;
tx_buf[3] = 0;
spi_write_then_read(spi_flash, tx_buf, 4, rx_buf, 2);
*pMID = rx_buf[0];
*pDID = rx_buf[1];
}
static void SPIFlashWriteEnable(int enable)
{
unsigned char val = enable ? 0x06 : 0x04;
//printk("%s\n",__func__);
spi_write(spi_flash, &val, 1);
}
static unsigned char SPIFlashReadStatusReg1(void)
{
unsigned char val;
unsigned char cmd = 0x05;
//printk("%s\n",__func__);
spi_write_then_read(spi_flash, &cmd, 1, &val, 1);
return val;
}
static unsigned char SPIFlashReadStatusReg2(void)
{
unsigned char val;
unsigned char cmd = 0x35;
//printk("%s\n",__func__);
spi_write_then_read(spi_flash, &cmd, 1, &val, 1);
return val;
}
static void SPIFlashWaitWhenBusy(void)
{
//printk("%s\n",__func__);
while (SPIFlashReadStatusReg1() & 1)
{
/* 休眠一段时间 */
/* Sector erase time : 60ms
* Page program time : 0.7ms
* Write status reg time : 10ms
*/
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(HZ / 100); /* 休眠10MS后再次判断 */
}
}
static void SPIFlashWriteStatusReg(unsigned char reg1, unsigned char reg2)
{
unsigned char tx_buf[4];
//printk("%s\n",__func__);
SPIFlashWriteEnable(1);
tx_buf[0] = 0x01;
tx_buf[1] = reg1;
tx_buf[2] = reg2;
spi_write(spi_flash, tx_buf, 3);
SPIFlashWaitWhenBusy();
}
static void SPIFlashClearProtectForStatusReg(void)
{
unsigned char reg1, reg2;
//printk("%s\n",__func__);
reg1 = SPIFlashReadStatusReg1();
reg2 = SPIFlashReadStatusReg2();
reg1 &= ~(1 << 7);
reg2 &= ~(1 << 0);
SPIFlashWriteStatusReg(reg1, reg2);
}
static void SPIFlashClearProtectForData(void)
{
/* cmp=0,bp2,1,0=0b000 */
unsigned char reg1, reg2;
//printk("%s\n",__func__);
reg1 = SPIFlashReadStatusReg1();
reg2 = SPIFlashReadStatusReg2();
reg1 &= ~(7 << 2);
reg2 &= ~(1 << 6);
SPIFlashWriteStatusReg(reg1, reg2);
}
/* erase 4K */
void SPIFlashEraseSector(unsigned int addr)
{
unsigned char tx_buf[4];
//printk("%s\n",__func__);
tx_buf[0] = 0x20;
tx_buf[1] = addr >> 16;
tx_buf[2] = addr >> 8;
tx_buf[3] = addr & 0xff;
SPIFlashWriteEnable(1);
spi_write(spi_flash, tx_buf, 4);
SPIFlashWaitWhenBusy();
}
/* program */
void SPIFlashProgram(unsigned int addr, unsigned char *buf, int len)
{
unsigned char tx_buf[4];
struct spi_transfer t[] =
{
{
.tx_buf = tx_buf,
.len = 4,
},
{
.tx_buf = buf,
.len = len,
},
};
struct spi_message m;
//printk("%s\n",__func__);
tx_buf[0] = 0x02;
tx_buf[1] = addr >> 16;
tx_buf[2] = addr >> 8;
tx_buf[3] = addr & 0xff;
SPIFlashWriteEnable(1);
spi_message_init(&m);
spi_message_add_tail(&t[0], &m);
spi_message_add_tail(&t[1], &m);
spi_sync(spi_flash, &m);
SPIFlashWaitWhenBusy();
}
void SPIFlashRead(unsigned int addr, unsigned char *buf, int len)
{
/* spi_write_then_read规定了tx_cnt+rx_cnt < 32
* 所以对于大量数据的读取,不能使用该函数
*/
unsigned char tx_buf[4];
struct spi_transfer t[] =
{
{
.tx_buf = tx_buf,
.len = 4,
},
{
.rx_buf = buf,
.len = len,
},
};
struct spi_message m;
//printk("%s\n",__func__);
tx_buf[0] = 0x03;
tx_buf[1] = addr >> 16;
tx_buf[2] = addr >> 8;
tx_buf[3] = addr & 0xff;
spi_message_init(&m);
spi_message_add_tail(&t[0], &m);
spi_message_add_tail(&t[1], &m);
spi_sync(spi_flash, &m);
}
static void SPIFlashInit(void)
{
SPIFlashClearProtectForStatusReg();
SPIFlashClearProtectForData();
}
/* 构造注册一个mtd_info
* mtd_device_register(master, parts, nr_parts)
*
*/
/* 首先: 构造注册spi_driver
* 然后: 在spi_driver的probe函数里构造注册mtd_info
*/
static struct mtd_info spi_flash_dev;
static int spi_flash_erase(struct mtd_info *mtd, struct erase_info *instr)
{
unsigned int addr = instr->addr;
unsigned int len = 0;
//判断参数
if ((addr & (spi_flash_dev.erasesize - 1)) || (instr->len & (spi_flash_dev.erasesize - 1)))
{
printk("spi_flash_erase addr/len is not aligned %x %x\n",(unsigned int)instr->addr,(unsigned int)instr->len);
return -EINVAL;
}
for (len = 0; len < instr->len; len += 4096)
{
SPIFlashEraseSector(addr);
addr += 4096;
}
instr->state = MTD_ERASE_DONE;
mtd_erase_callback(instr);
return 0;
}
static int spi_flash_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
SPIFlashRead(from, buf, len);
*retlen = len;
return 0;
}
static int spi_flash_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
unsigned int addr = to;
unsigned int wlen = 0;
unsigned int i;
//判断参数
if ((to & (spi_flash_dev.erasesize - 1)))
{
printk("spi_flash_write addr/len is not aligned %x %x\n",(unsigned int)to,(unsigned int)len);
return -EINVAL;
}
if (len <= 256)
{
SPIFlashProgram(addr, (unsigned char *)buf, len);
}
else
{
for (i = 0; i < len / 256; i ++)
{
SPIFlashProgram(addr, (unsigned char *)buf, 256);
addr += 256;
buf += 256;
}
if (len % 256 != 0)
{
SPIFlashProgram(addr, (unsigned char *)buf, len % 256);
}
}
*retlen = len;
return 0;
}
static int spi_flash_probe(struct spi_device *spi)
{
int mid, did, ret;
spi_flash = spi;
spi->mode = SPI_MODE_0;
ret = spi_setup(spi);
if (ret < 0)
{
printk("spi_setup error\n");
}
printk("%s\n",__func__);
//s3c2410_gpio_cfgpin(spi->chip_select, S3C2410_GPIO_OUTPUT);
SPIFlashInit();
SPIFlashReadID(&mid, &did);
printk("SPI Flash ID: %02x %02x\n", mid, did);
memset(&spi_flash_dev, 0, sizeof(spi_flash_dev));
//Setup the MTD structure
spi_flash_dev.name = "spi_flash";
spi_flash_dev.type = MTD_NORFLASH;
spi_flash_dev.flags = MTD_CAP_NORFLASH;
spi_flash_dev.size = 0x800000;
spi_flash_dev.writesize = 1;
spi_flash_dev.writebufsize = 4096;
spi_flash_dev.erasesize = 4096;
spi_flash_dev.owner = THIS_MODULE;
spi_flash_dev._erase = spi_flash_erase;
spi_flash_dev._read = spi_flash_read;
spi_flash_dev._write = spi_flash_write;
mtd_device_register(&spi_flash_dev, NULL, 0);
return 0;
}
static int spi_flash_remove(struct spi_device *spi)
{
mtd_device_unregister(&spi_flash_dev);
return 0;
}
static const struct of_device_id spi_flash_ids[] = {
{ .compatible = "tiny4412,spi_flash" },
{ }
};
static struct spi_driver spi_flash_drv =
{
.driver = {
.name = "spi_flash",
},
.probe = spi_flash_probe,
.remove = spi_flash_remove,
};
static int spi_flash_init(void)
{
printk("init\n");
return spi_register_driver(&spi_flash_drv);
}
static void spi_flash_exit(void)
{
spi_unregister_driver(&spi_flash_drv);
}
module_init(spi_flash_init);
module_exit(spi_flash_exit);
MODULE_LICENSE("GPL");