设备树学习之(十)spi flash

开发板: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");

你可能感兴趣的:(Device,Tree,Tiny4412,DTS之Tiny4412)