开发板:tiny4412SDK + S702 + 4GB Flash?
要移植的内核版本:Linux-4.4.0 (支持device tree)?
u-boot版本:友善之臂自带的 U-Boot 2010.12?
busybox版本:busybox 1.25
目标:?
驱动外接的8M的 spi flash,注册为块设备。
设备树:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
&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 >;
};
};
};
这里指定的 reg = <0> 表示的该 spi 设备引用第一个 cs-gpios?
spi-max-frequency = <10000000>;表示最大速率
代码:
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/media.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/videodev2.h>
#include <media/media-entity.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-mediabus.h>
#include <media/s5c73m3.h>
#include <media/v4l2-of.h>
#include <linux/mtd/mtd.h>
static struct spi_device *spi_flash;
void SPIFlashReadID(int *pMID,int *pDID)
{
unsigned char tx_buf[4];
char rx_buf[2];
tx_buf[0] = 0x90;
tx_buf[1] = 0;
tx_buf[2] = 3] = 0;
spi_write_then_read(spi_flash,tx_buf,4,rx_buf,102); box-sizing: border-box;">2);
*pMID = rx_buf[0];
*pDID = rx_buf[1];
}
void SPIFlashWriteEnable(int enable)
{
char val = enable ? 0x06 : 0x04;
);
}
char SPIFlashReadStatusReg1(void)
{
char val;
char cmd = 0x05;
,102); box-sizing: border-box;">1);
return val;
}
char SPIFlashReadStatusReg2(0x35;
SPIFlashWaitWhenBusy(void)
{
(SPIFlashReadStatusReg1() & 1)
{
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(HZ / 100);
}
}
void SPIFlashWriteStatusReg(char reg1,136); box-sizing: border-box;">char reg2)
{
4];
);
tx_buf[0x01;
tx_buf[1] = reg1;
tx_buf[2] = reg2;
spi_write(spi_flash,102); box-sizing: border-box;">3);
SPIFlashWaitWhenBusy();
}
void SPIFlashClearProtectForStatusReg(1 << 7);
reg2 &= ~(0);
SPIFlashWriteStatusReg(reg1,reg2);
}
void SPIFlashClearProtectForData(/* cmp=0,bp2,1,0=0b000 */
7 << 2);
reg2 &= ~(6);
SPIFlashWriteStatusReg(reg1,reg2);
}
void SPIFlashEraseSector(int addr)
{
0x20;
tx_buf[1] = addr >> 16;
tx_buf[2] = addr >> 8;
tx_buf[3] = addr & 0xff;
SPIFlashWriteEnable(1);
spi_write(spi_flash,102); box-sizing: border-box;">4);
SPIFlashWaitWhenBusy();
}
void SPIFlashProgram(int addr,136); box-sizing: border-box;">char *buf,136); box-sizing: border-box;">int len)
{
struct spi_transfer t[] =
{
{
.tx_buf = tx_buf,.len = struct spi_message m;
;
tx_buf[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(int len)
{
0x03;
tx_buf[0xff;
spi_message_init(&m);
spi_message_add_tail(&t[void SPIFlashInit(void)
{
SPIFlashClearProtectForStatusReg();
SPIFlashClearProtectForData();
}
struct mtd_info spi_flash_dev;
int spi_flash_erase(struct mtd_info *mtd,136); box-sizing: border-box;">struct erase_info *instr)
{
int addr = instr->addr;
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 %xn",(int)instr->addr,136); box-sizing: border-box;">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;
}
int spi_flash_read(int spi_flash_write(const u_char *buf)
{
int addr = to;
int wlen = 0;
int i;
if ((to & (spi_flash_dev.erasesize - "spi_flash_write addr/len is not aligned %x %xn",136); box-sizing: border-box;">int)to,136); box-sizing: border-box;">int)len);
return -EINVAL;
}
if (len <= 256)
{
SPIFlashProgram(addr,136); box-sizing: border-box;">char *)buf,len);
}
else
{
for (i = 0; i < len / 256; i ++)
{
SPIFlashProgram(addr,102); box-sizing: border-box;">256);
addr += 256;
buf += 256;
}
if (len % 256 != 0)
{
SPIFlashProgram(addr,len % 256);
}
}
*retlen = len;
0;
}
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 errorn");
}
printk("%sn",__func__);
SPIFlashInit();
SPIFlashReadID(&mid,&did);
printk("SPI Flash ID: %02x %02xn",mid,did);
memset(&spi_flash_dev,102); box-sizing: border-box;">0,136); box-sizing: border-box;">sizeof(spi_flash_dev));
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,102); box-sizing: border-box;">0);
int spi_flash_remove(struct spi_device *spi)
{
mtd_device_unregister(&spi_flash_dev);
const struct of_device_id spi_flash_ids[] = {
{ .compatible = },{ }
};
struct spi_driver spi_flash_drv =
{
.driver = {
.name = "spi_flash",.probe = spi_flash_probe,.remove = spi_flash_remove,};
int spi_flash_init(void)
{
printk("initn");
return spi_register_driver(&spi_flash_drv);
}
void spi_flash_exit(void)
{
spi_unregister_driver(&spi_flash_drv);
}
module_init(spi_flash_init);
module_exit(spi_flash_exit);
MODULE_LICENSE("GPL");