1. 程式人生 > >Device Tree(七)NAND FLASH

Device Tree(七)NAND FLASH

1、新增裝置節點

2、新增分割槽資訊

3、新增nand驅動檔案編譯選項

Louis_nand.h

#ifndef __LINUX210_NAND_H
#define __LINUX210_NAND_H

#define S5P_NFCONF			0x00
#define S5P_NFCONT			0x04
#define S5P_NFCMD			0x08
#define S5P_NFADDR			0x0c
#define S5P_NFDATA			0x10
#define S5P_NFMECCDATA0		0x14
#define S5P_NFMECCDATA1		0x18
#define S5P_NFSECCDATA		0x1c
#define S5P_NFSBLK			0x20
#define S5P_NFEBLK			0x24
#define S5P_NFSTAT			0x28
#define S5P_NFMECCERR0		0x2c
#define S5P_NFMECCERR1		0x30
#define S5P_NFMECC0			0x34
#define S5P_NFMECC1			0x38
#define S5P_NFSECC			0x3c
#define S5P_NFMLCBITPT		0x40
#define S5P_NF8ECCERR0		0x44 
#define S5P_NF8ECCERR1  	0x48
#define S5P_NF8ECCERR2		0x4C           
#define S5P_NFM8ECC0		0x50         
#define S5P_NFM8ECC1		0x54        
#define S5P_NFM8ECC2		0x58       
#define S5P_NFM8ECC3		0x5C
#define S5P_NFMLC8BITPT0	0x60
#define S5P_NFMLC8BITPT1	0x64

#define S5P_NFECCCONF		0x00
#define S5P_NFECCCONT		0x20
#define S5P_NFECCSTAT		0x30
#define S5P_NFECCSECSTAT	0x40
#define S5P_NFECCPRGECC		0x90
#define S5P_NFECCERL		0xC0
#define S5P_NFECCERP		0xF0

#define S5P_NFCONF_NANDBOOT		(1 << 31)
#define S5P_NFCONF_ECCCLKCON	(1 << 30)
#define S5P_NFCONF_ECC_MLC		(1 << 24)
#define	S5P_NFCONF_ECC_1BIT		(0 << 23)
#define	S5P_NFCONF_ECC_4BIT		(2 << 23)
#define	S5P_NFCONF_ECC_8BIT		(1 << 23)
#define S5P_NFCONF_TACLS(x)		((x) << 12)
#define S5P_NFCONF_TWRPH0(x)	((x) << 8)
#define S5P_NFCONF_TWRPH1(x)	((x) << 4)
#define S5P_NFCONF_MLC			(1 << 3)
#define S5P_NFCONF_PAGESIZE		(1 << 2)
#define S5P_NFCONF_ADDRCYCLE	(1 << 1)
#define S5P_NFCONF_BUSWIDTH		(1 << 0)

#define S5P_NFCONT_ECC_ENC		(1 << 18)
#define S5P_NFCONT_LOCKTGHT		(1 << 17)
#define S5P_NFCONT_LOCKSOFT		(1 << 16)
#define S5P_NFCONT_MECCLOCK		(1 << 7)
#define S5P_NFCONT_SECCLOCK		(1 << 6)
#define S5P_NFCONT_INITMECC		(1 << 5)
#define S5P_NFCONT_INITSECC		(1 << 4)
#define S5P_NFCONT_nFCE1		(1 << 2)
#define S5P_NFCONT_nFCE0		(1 << 1)
#define S5P_NFCONT_MODE			(1 << 0)

#define S5P_NFSTAT_READY		(1 << 0)

#define S5P_NFECCCONT_MECCRESET	(1 << 0)
#define S5P_NFECCCONT_MECCINIT	(1 << 2)
#define S5P_NFECCCONT_ECCDIRWR	(1 << 16)

#define S5P_NFECCSTAT_ECCBUSY	(1 << 31)


enum Louis_cpu_type {
    TYPE_LOUIS210,              
};

struct Louis_nand_host {
    struct nand_chip    nand_chip;      //硬體操作函式集合
    void __iomem        *nf_base;       //暫存器基地址
    void __iomem        *ecc_base;      //ECC基地址
    struct clk          *clk[2];        //時鐘
    enum  Louis_cpu_type    cpu_type;   //CPU型別
};

#endif

Louis_nand.c

/*
 * Copyright (c) 2018 LouisGou <[email protected]>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * Louis MTD NAND driver
*/


#include <linux/module.h>
#include <linux/delay.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/of_platform.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/jiffies.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/io.h>

#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include "Louis210_nand.h"


/* 用於檢查裝置就緒狀態 */ 
static int Louis_nand_device_ready(struct mtd_info *mtd)
{
    struct nand_chip *nand_chip = mtd->priv;
    struct Louis_nand_host *host = nand_chip->priv;

    /* it's to check the RnB nand signal bit and
	 * return to device ready condition in nand_base.c
	 */
    
    //return "NFSTAT的暫存器[0] Flash_RnB";
    return readl(host->nf_base + S5P_NFSTAT) & S5P_NFSTAT_READY;
}

/* 命令控制函式 */
static void Louis_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
    struct nand_chip *nand_chip = mtd->priv;
	struct Louis_nand_host *host = nand_chip->priv;

    if (cmd == NAND_CMD_NONE)
        return;

    if (ctrl & NAND_CLE){
        /* 發命令: NFCMMD = dat */
        writeb(cmd, host->nf_base + S5P_NFCMD);
	}
	else{
        /* 發地址: NFADDR = dat */
		writeb(cmd, host->nf_base + S5P_NFADDR);
	}
}

/*片選函式*/
static void Louis_nand_select_chip(struct mtd_info *mtd, int chip)
{
    struct nand_chip *nand_chip = mtd->priv;
    struct Louis_nand_host *host = nand_chip->priv;
    u32 value = readl(host->nf_base + S5P_NFCONT);

    if(chip == -1){
        /* 取消選中: NFCONT[1]設為1 */
        value |= S5P_NFCONT_nFCE0;	/* deselect */
	}
	else{
        /* 選中: NFCONT[1]設為0 */
		value &= ~S5P_NFCONT_nFCE0;	/* select */
	}

    writel(value, host->nf_base + S5P_NFCONT);
}    

static void Louis_nand_inithw_later(struct mtd_info *mtd)
{
    struct nand_chip *nand_chip = mtd->priv;
    struct Louis_nand_host *host = nand_chip->priv;
    u32 value;

    value = readl(host->nf_base + S5P_NFCONF);

    if(nand_is_slc(nand_chip)){
        value &= ~S5P_NFCONF_MLC;

        if(mtd->writesize == 512){
            value |= S5P_NFCONF_PAGESIZE;
        } else  {
            value &= ~S5P_NFCONF_PAGESIZE;
        }
    } else {
        value |= S5P_NFCONF_MLC;

        if(mtd->writesize == 4096){
            value &= ~S5P_NFCONF_PAGESIZE;
        } else  {
            value |= S5P_NFCONF_PAGESIZE;
        }
    }
}

static void Louis_nand_inithw(struct Louis_nand_host *host)
{
    u32 value;

    //使能NAND控制
    value = readl(host->nf_base + S5P_NFCONT);
    writel(value | S5P_NFCONT_MODE, host->nf_base + S5P_NFCONT);
}

static void Louis_nand_parse_dt(struct Louis_nand_host *host, struct device *dev)
{
    host->cpu_type = (enum Louis_cpu_type)of_device_get_match_data(dev);
}

static int Louis_nand_probe(struct platform_device *pdev)
{
    int ret;
    struct Louis_nand_host  *host;
    struct nand_chip *nand_chip;
    struct mtd_info *mtd;
    struct resource *mem;

    printk(KERN_INFO "Here I am: %s:%i\n", __FILE__, __LINE__);

    host = devm_kzalloc(&pdev->dev, sizeof(struct Louis_nand_host), GFP_KERNEL);

    if(!host)
        return  ENOMEM;

    mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);       //分配memory資源
    host->nf_base = devm_ioremap_resource(&pdev->dev, mem);     //記憶體對映

    if(IS_ERR(host->nf_base)){  //判斷是否是錯誤指標
        return PTR_ERR(host->nf_base);  //返回錯誤程式碼
    }

    mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);       //分配memory資源
    host->ecc_base = devm_ioremap_resource(&pdev->dev, mem);     //記憶體對映

    if(IS_ERR(host->ecc_base)){  //判斷是否是錯誤指標
        return PTR_ERR(host->ecc_base);  //返回錯誤程式碼
    }

    nand_chip = &host->nand_chip;
    nand_chip->priv = host;
    nand_set_flash_node(nand_chip, pdev->dev.of_node);  //???

    mtd = nand_to_mtd(nand_chip);
    mtd->dev.parent = &pdev->dev;
    mtd->priv = nand_chip;

    // 禁用晶片選擇和啟用NAND快閃記憶體控制器 
    writel((0x01 << 1) | (0x01 << 0), host->nf_base + S5P_NFCONT);


    // 設定NAND IO行的地址 
    nand_chip->IO_ADDR_R = host->nf_base + S5P_NFDATA;
    nand_chip->IO_ADDR_W = host->nf_base + S5P_NFDATA;

    platform_set_drvdata(pdev, host);

    // 獲取時鐘源並使能(??? 為什麼是兩個)
    host->clk[0] = devm_clk_get(&pdev->dev, "nandxl");
    if(IS_ERR(host->clk[0])){
        dev_err(&pdev->dev, "cannot get clock of nandxl\n");
        return -ENOENT;
    }
    clk_prepare_enable(host->clk[0]);

    host->clk[1] = devm_clk_get(&pdev->dev, "nand");
    if(IS_ERR(host->clk[1])){
        dev_err(&pdev->dev, "cannot get clock of nand\n");
        return -ENOENT;
    }
    clk_prepare_enable(host->clk[1]);

    //獲取CPU型別
    Louis_nand_parse_dt(host, &pdev->dev);

    nand_chip->select_chip = Louis_nand_select_chip; 
	nand_chip->cmd_ctrl = Louis_nand_cmd_ctrl;
	nand_chip->dev_ready = Louis_nand_device_ready;

    Louis_nand_inithw(host);

    ret = nand_scan_with_ids(mtd, 1, NULL);

    if(ret)
        return ret;
    
    /*if(nand_chip->ecc.mode == NAND_ECC_HW){
        nand_chip->ecc.correct = s5pcxx_nand_correct_data;
		nand_chip->ecc.calculate = s5pcxx_nand_calculate_ecc;
		nand_chip->ecc.hwctl = s5pcxx_nand_enable_hwecc;
		nand_chip->ecc.read_page = s5pcxx_nand_read_page_hwecc;

		nand_chip->ecc.size = 512;
		nand_chip->ecc.bytes = 13;

		mtd_set_ooblayout(nand_to_mtd(nand_chip), &s5pcxx_ooblayout_ops);
    }  */

    /*ret = nand_scan_tail(mtd);*/

    //獲取實際的硬體資訊之後
    Louis_nand_inithw_later(mtd);

    //mtd註冊
    return mtd_device_register(mtd, NULL,0);
}

static int Louis_nand_remove(struct platform_device *pdev)
{
    struct Louis_nand_host *host = platform_get_drvdata(pdev);
    struct mtd_info *mtd = nand_to_mtd(&host->nand_chip);

    nand_release(mtd);
    clk_disable_unprepare(host->clk[0]);
    clk_disable_unprepare(host->clk[1]);

    return 0; 
}

static const struct of_device_id Louis_nand_match[] = {
    {.compatible = "samsung,Louis210-nand", .data = TYPE_LOUIS210},
    {},
};
MODULE_DEVICE_TABLE(of, Louis_nand_match);

static struct platform_driver Louis_nand_driver = {
    .probe      = Louis_nand_probe,
    .remove     = Louis_nand_remove,
    .driver     = {
        .name   = "Louis-nand",
        .owner  = THIS_MODULE,
        .of_match_table     = Louis_nand_match,
    },
};
module_platform_driver(Louis_nand_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("LouisGou <
[email protected]
>"); MODULE_DESCRIPTION("Louis MTD NAND driver");