Linux SPI驱动学习——调用SPI读写函数
- 博客说明
- 开发环境
- 1. 基于W25Q16型FLASH来看SPI的读写函数
- 附录
博客说明
撰写日期 |
2019.10.25 |
完稿日期 |
未完稿 |
最近维护 |
暂无 |
本文作者 |
multimicro |
联系方式 |
[email protected] |
资料链接 |
本文无附件资料 |
GitHub |
https://github.com/wifialan/drivers/blob/master/w25q16_spi.c |
原文链接 |
https://blog.csdn.net/multimicro/article/details/102738026 |
开发环境
环境说明 |
详细信息 |
备注信息 |
操作系统 |
Ubunut 18.04 |
|
开发板 |
JZ2440-V3 |
|
Linux内核 |
linux-3.4.2 |
|
1. 基于W25Q16型FLASH来看SPI的读写函数
这里将w25q16注册为了一个mtd设备
,和字符设备
最大不同点就是,不同的设备类型中的方法不同,对于flash硬件,注册为mtd设备
会更加便于操作。这里给出源码供分析参考:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DRV_NAME "s3c2440_w25q16"
#define DRV_AUTHOR "Alan Tian "
#define DRV_DESC "S3C2440 W25Q16 Driver"
static int w25q16_bus_spi_probe(struct spi_device *spi);
static int w25q16_spi_remove(struct spi_device *spi);
struct spi_master *spi_master_p;
struct spi_device *spi_flash;
struct spi_message *spi_mesg_p;
void SPIFlashReadID(int *pMID, int *pDID)
{
unsigned char tx_buf[4];
unsigned char rx_buf[2];
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;
spi_write(spi_flash, &val, 1);
}
static unsigned char SPIFlashReadStatusReg1(void)
{
unsigned char val;
unsigned char cmd = 0x05;
spi_write_then_read(spi_flash, &cmd, 1, &val, 1);
return val;
}
static unsigned char SPIFlashReadStatusReg2(void)
{
unsigned char val;
unsigned char cmd = 0x35;
spi_write_then_read(spi_flash, &cmd, 1, &val, 1);
return val;
}
static void SPIFlashWaitWhenBusy(void)
{
while (SPIFlashReadStatusReg1() & 1)
{
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(HZ/100);
}
}
static void SPIFlashWriteStatusReg(unsigned char reg1, unsigned char reg2)
{
unsigned char tx_buf[4];
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;
reg1 = SPIFlashReadStatusReg1();
reg2 = SPIFlashReadStatusReg2();
reg1 &= ~(1<<7);
reg2 &= ~(1<<0);
SPIFlashWriteStatusReg(reg1, reg2);
}
static void SPIFlashClearProtectForData(void)
{
unsigned char reg1, reg2;
reg1 = SPIFlashReadStatusReg1();
reg2 = SPIFlashReadStatusReg2();
reg1 &= ~(7<<2);
reg2 &= ~(1<<6);
SPIFlashWriteStatusReg(reg1, reg2);
}
void SPIFlashEraseSector(unsigned int addr)
{
unsigned char tx_buf[4];
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();
}
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;
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)
{
unsigned char tx_buf[4];
struct spi_transfer t[] = {
{
.tx_buf = tx_buf,
.len = 4,
},
{
.rx_buf = buf,
.len = len,
},
};
struct spi_message m;
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();
}
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("addr/len is not aligned\n");
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;
if ((to & (spi_flash_dev.erasesize - 1)) || (len & (spi_flash_dev.erasesize - 1)))
{
printk("addr/len is not aligned\n");
return -EINVAL;
}
for (wlen = 0; wlen < len; wlen += 256)
{
SPIFlashProgram(addr, (unsigned char *)buf, 256);
addr += 256;
buf += 256;
}
*retlen = len;
return 0;
}
static struct spi_driver w25q16_spi_driver =
{
.driver =
{
.name = "w25q16",
.owner = THIS_MODULE,
},
.probe = w25q16_bus_spi_probe,
.remove = __devexit_p(w25q16_spi_remove),
};
static int __devinit w25q16_bus_spi_probe(struct spi_device *spi)
{
int mid, did;
printk("w25q16 probe function\n");
spi_flash = spi;
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));
spi_flash_dev.name = "w25q16_flash";
spi_flash_dev.type = MTD_NORFLASH;
spi_flash_dev.flags = MTD_CAP_NORFLASH;
spi_flash_dev.size = 0x200000;
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 w25q16_spi_remove(struct spi_device *spi)
{
mtd_device_unregister(&spi_flash_dev);
return 0;
}
static int __init w25q16_driver_init(void)
{
int ret;
printk("\n************ driver init begin ************\n\n");
ret = spi_register_driver(&w25q16_spi_driver);
if(ret)
{
spi_unregister_driver(&w25q16_spi_driver);
printk(DRV_NAME "\tFailed register spi driver. Error: %d\n",ret);
}
printk("\n************* driver init end *************\n\n");
return ret;
}
static void __exit w25q16_driver_exit(void)
{
printk("Unregister w25q16 flash!\n");
spi_unregister_driver(&w25q16_spi_driver);
}
module_init(w25q16_driver_init);
module_exit(w25q16_driver_exit);
MODULE_AUTHOR( DRV_AUTHOR );
MODULE_DESCRIPTION(DRV_DESC);
MODULE_LICENSE("GPL");
附录
- Github: w25q16_spi.c