MTD(Memory Technology Device) -2-

<kernel v4.14>

NAND 디바이스 트리

NAND 호스트 컨트롤러 관련 기본 속성들

  • compatible
    • 드라이버 compatiblity 문자열
    • 예) “brcm,brcmnand-v6.1”
  • reg
    • 첫 번째 기재된 주소는 NAND 레지스터 시작 물리 주소
    • 그외 옵션
      • DMA를 사용하는 경우 Flash DMA 레지스터 시작 물리 주소
      • NAND 캐시를 사용하는 경우 캐시 레지스터 시작 물리 주소
  • reg-names
    • 위의 주어진 레지스터를 설명하는 문자열
    • 예) “nand”, “flash-dma”, nand-cache”
  • interrupts
    • NAND 컨트롤러와 연결될 인터럽트 컨트롤러와 인터럽트 번호
  • interrupt-names
    • 위의 인터럽트를 설명하는 문자열
    • 예) “nand-ctlrdy”, “flash_dma_done” 등
  • interrupt-parent
    • 상위 인터럽트 컨트롤러
  • #address-cells
    • 칩 셀렉트 번호로 항상 1
  • #size-cells
    • 항상 0

 

NAND 칩 관련 서브 노드 속성들

  • compatible
    • “brcm,nandcs”
  • reg
    • 칩 셀렉트 번호로 0, 1, 2…
  • #address-calls
    • 하위 노드인 파티션 노드의 주소 표현에 필요한 4바이트 수
      • 1 = 32bit 주소(4 바이트)
      • 2 = 64bit 주소(8 바이트)
  • #size-cells
    • 하위 노드인 파티션 노드의 사이즈 표현에 필요한 4바이트 수
      • 1 = 32bit 주소(4 바이트)
      • 2 = 64bit 주소(8 바이트)
  • nand-on-flash-bbt
    • NAND에 BBT(Bad Block Table)를 지원하는지 여부
      • “true” or “false”
  • nand-ecc-mode
    • NAND ECC 모드 문자열로 다음과 같다.
      • “none”
      • “soft”
        • 부트로더 또는 커널 드라이버 software가 관리
      • “hw”
        • 호스트 컨트롤러가 관리
      • “hw_syndrome”
        • sunxi 사의 sun7i에 탑재된 nand 호스트 컨트롤러에서 관리
      • “hw_oob_first”
        • OOB를 먼저 읽어야 하는 경우 사용한다.
      • “on-die”
        • 퓨젼 NAND 칩에 내장된 ECC 컨트롤러가 관리
      • “soft_bch”
        • deprecate
  • nand-ecc-algo
    • NAND ECC 알고리즘 문자열로 다음과 같다.
      • “hamming”
      • “bch”
  • nand-bus-width
    • NAND 데이터 버스 사이즈
      • 8 = x8
      • 16 = x16
  • nand-ecc-strength
    • ECC 한 스텝당 correction 가능한 비트 수
    • 1, 5, 6, 8, 10, 12, 14, 16, …
  • nand-ecc-step-size
    • ECC 한 스텝 사이즈
      • 512 또는 1024
  • nand-ecc-maximize
    • ECC strength를 최대치로 사용할지의 여부
    • “true” or “false”

 

파티션 관련 서브 노드 속성들

  • compatible
    • “fixed-partions”만 지원한다.
  • reg
    • 파티션 시작 주소 및 사이즈

옵션 속성들

  • label
    • label 명
  • read-only
    • read-only로 마운트
  • lock
    • 초기화 시 unlock 불가능하게 하는 옵션

 

Broadcom

Case 1) 

nand@f0442800 {
        compatible = "brcm,brcmnand-v7.0", "brcm,brcmnand";
        reg = <0xF0442800 0x600>,
              <0xF0443000 0x100>;
        reg-names = "nand", "flash-dma";
        interrupt-parent = <&hif_intr2_intc>;
        interrupts = <24>, <4>;

        #address-cells = <1>;
        #size-cells = <0>;

        nandcs@1 {
                compatible = "brcm,nandcs";
                reg = <1>; // Chip select 1
                nand-on-flash-bbt;
                nand-ecc-strength = <12>;
                nand-ecc-step-size = <512>;

                // Partitions
                #address-cells = <1>;  // <2>, for 64-bit offset
                #size-cells = <1>;     // <2>, for 64-bit length
                flash0.rootfs@0 {
                        reg = <0 0x10000000>;
                };
                flash0@0 {
                        reg = <0 0>; // MTDPART_SIZ_FULL
                };
                flash0.kernel@10000000 {
                        reg = <0x10000000 0x400000>;
                };
        };
};
  • BBT가 지원되며, 512바이트 당 12bit의 ECC(BCH-12)

 

Case 2)
nand@10000200 {
        compatible = "brcm,nand-bcm63168", "brcm,nand-bcm6368",
                "brcm,brcmnand-v4.0", "brcm,brcmnand";
        reg = <0x10000200 0x180>,
              <0x10000600 0x200>,
              <0x100000b0 0x10>;
        reg-names = "nand", "nand-cache", "nand-int-base";
        interrupt-parent = <&periph_intc>;
        interrupts = <50>;
        clocks = <&periph_clk 20>;
        clock-names = "nand";

        #address-cells = <1>;
        #size-cells = <0>;

        nand0: nandcs@0 {
                compatible = "brcm,nandcs";
                reg = <0>;
                nand-on-flash-bbt;
                nand-ecc-strength = <1>;
                nand-ecc-step-size = <512>;
        };
};
  • brcm,nand-oob-sector-size
    • OOB 사이즈
      • 16, 32, 64, 128, 256, …
  • BBT가 지원되며, 512바이트 당 1bit의 ECC

 

 

파티션

부팅 시 표현되는 mtd 정보

다음 두 가지의 디바이스가 탑재된 사례이다.

  • Micron NAND 플래시: MT29F16G08ABABAWP 
  • Micron NOR 플래시: m25p80
nand: device found, Manufacturer ID: 0x2c, Chip ID: 0x48
nand: Micron MT29F16G08ABABAWP
nand: 2048 MiB, SLC, erase size: 512 KiB, page size: 4096, OOB size: 224
iproc_nand 66460000.nand: detected 2048MiB total, 512KiB blocks, 4KiB pages, 27B OOB, 8-bit, BCH-8
Scanning device for bad blocks
random: nonblocking pool is initialized
6 ofpart partitions found on MTD device brcmnand.0
Creating 6 MTD partitions on "brcmnand.0":
0x000000000000-0x000000700000 : "nboot"
0x000000700000-0x000000800000 : "nenv"
0x000000800000-0x000001000000 : "bootconf"
0x000001000000-0x000011000000 : "os1"
0x000011000000-0x000021000000 : "os2"
0x000021000000-0x00003f000000 : "ndata"

bcmspi_setup:628 cru_control 0x6010000
m25p80 spi1.0: n25q256a (32768 Kbytes)
4 ofpart partitions found on MTD device spi1.0
Creating 4 MTD partitions on "spi1.0":
0x000000000000-0x0000001d0000 : "bootloader"
0x0000001d0000-0x0000001f0000 : "env"
0x000000200000-0x000002000000 : "reserved"
0x000001000000-0x000002000000 : "data"

 

proc 인터페이스

다음과 같이 구성되어 있다.

  • NAND
    • mtd0 ~ mtd5
  • NOR
    • mtd6 ~ mtd9
# cat /proc/mtd
dev:    size   erasesize  name
mtd0: 00700000 00080000 "nboot"
mtd1: 00100000 00080000 "nenv"
mtd2: 00800000 00080000 "bootconf"
mtd3: 10000000 00080000 "os1"
mtd4: 10000000 00080000 "os2"
mtd5: 1e000000 00080000 "ndata"
mtd6: 001d0000 00001000 "bootloader"
mtd7: 00020000 00001000 "env"
mtd8: 01e00000 00001000 "reserved"
mtd9: 01000000 00001000 "data"

 

device 파일

아래와 같이 디바이스 파일들이 생성된다.

# ls /dev/mtd?
mtd0  mtd1  mtd2  mtd3  mtd4  mtd5  mtd6  mtd7  mtd8  mtd9

# ls /dev/mtd?ro
mtd0ro  mtd1ro  mtd2ro  mtd3ro  mtd4ro  mtd5ro  mtd6ro  mtd7ro  mtd8ro  mtd9ro

# ls /dev/mtdblock*
mtdblock0   mtdblock1   mtdblock10  mtdblock2   mtdblock3   mtdblock4   mtdblock5   mtdblock6   mtdblock7   mtdblock8   mtdblock9

 

mtd-utils

파티션 정보 확인
# mtdinfo
Count of MTD devices:           10
Present MTD devices:            mtd0, mtd1, mtd2, mtd3, mtd4, mtd5, mtd6, mtd7, mtd8, mtd9
Sysfs interface supported:      yes
  • -a 옵션을 사용하는 경우 전체 파티션에 대한 세부 정보를 볼 수 있다.

 

특정 파티션에 대한 세부 정보를 보려면 다음과 같이한다.

# mtdinfo /dev/mtd0
mtd0
Name:                           nboot
Type:                           nand
Eraseblock size:                262144 bytes, 256.0 KiB
Amount of eraseblocks:          28 (7340032 bytes, 7.0 MiB)
Minimum input/output unit size: 4096 bytes
Sub-page size:                  4096 bytes
OOB size:                       216 bytes
Character device major/minor:   90:0
Bad blocks are allowed:         true
Device is writable:             false

 

nanddump
# nanddump
Usage: nanddump [OPTIONS] MTD-device
Dumps the contents of a nand mtd partition.

           --help               Display this help and exit
           --version            Output version information and exit
           --bb=METHOD          Choose bad block handling method (see below).
-a         --forcebinary        Force printing of binary data to tty
-c         --canonicalprint     Print canonical Hex+ASCII dump
-f file    --file=file          Dump to file
-l length  --length=length      Length
-n         --noecc              Read without error correction
           --omitoob            Omit OOB data (default)
-o         --oob                Dump OOB data
-p         --prettyprint        Print nice (hexdump)
-q         --quiet              Don't display progress and status messages
-s addr    --startaddress=addr  Start address
-b         --checkbadblock      Check bad blocks from the start of device

--bb=METHOD, where METHOD can be `padbad', `dumpbad', or `skipbad':
    padbad:  dump flash data, substituting 0xFF for any bad blocks
    dumpbad: dump flash data, including any bad blocks
    skipbad: dump good data, completely skipping any bad blocks (default)
 

nanddump 유틸리티를 사용하여 0번 파티션을 파일로 Backup

  • -o 옵션을 사용하는 경우 oob까지 같이 dump
# nanddump /dev/mtd0 -f /tmp/bios.bak
ECC failed: 0
ECC corrected: 0
Number of bad blocks: 0
Number of bbt blocks: 0
Block size 262144, page size 4096, OOB size 216
Dumping data starting at 0x00000000 and ending at 0x00700000...

 

또는 다음과 같이 dd 명령으로도 백업을 받을 수 있다. 단 nanddump와 달리 배드 블럭이 있는 경우 포함되어 dump된다.

# dd if=/dev/mtd0ro of=bios.bak
16384+0 records in
16384+0 records out
8388608 bytes (8.4 MB) copied, 10.0269 s, 837 kB/s

 

Verify

0번 파티션과 피일간 비교

sha1sum /dev/mtd0ro bios.bak
    fdbb011920572ca6c991377c4b418a0502668b73  /dev/mtd0ro
    fdbb011920572ca6c991377c4b418a0502668b73  bios.bak

 

Erase

0번 파티션 erase

    # flash_erase /dev/mtd0 0 0
    Erasing 4 Kibyte @ 7ff000 -- 100 % complete

 

Write

0번 파티션에 파일을 기록

$ sudo nandwrite /dev/mtd0 nand.img
Writing data to block 0 at offset 0x0
Writing data to block 1 at offset 0x4000
Writing data to block 2 at offset 0x8000
Writing data to block 3 at offset 0xc000
...

 

또는 다음과 같이 dd 명령으로도 백업을 받을 수 있다. 단 nandwrite와 달리 배드 블럭을 무시하고 기록된다.

# dd if=MNW2MAX1.X64.0092.R01.1605221712.bin of=/dev/mtd0

 

/sys/class/mtd 사용

다음과 같이 mtd 파티션 중 nand 파티션 하나를 골라 출력을 하였다.

# ls /sys/class/mtd/mtd1
bad_blocks         dev                ecc_strength       numeraseregions    subpagesize        writesize
bbt_blocks         device             erasesize          offset             subsystem
bitflip_threshold  ecc_failures       flags              oobsize            type
corrected_bits     ecc_step_size      name               size               uevent

한 번의 Erase 에 사용되는 바이트 수 (= 1 블럭)

# cat erasesize
262144

 

한 번의 Write에 사용되는 바이트 수 (= 1 페이지)

# cat writesize
4096

 

이 파티션 내의 배드 블럭 수

# cat bad_blocks
0

 

이 파티션 내에 존재하는 bbt 블럭 수

# cat bbt_blocks
0

 

이 디바이스에서 읽기 동작 시 ECC 에러 발생 수 확인

# cat ecc_failures
0

 

이 디바이스에서 ECC correction한 비트 수

# cat corrected_bits
0

 

ECC 스텝이 사용할 바이트 수

# cat ecc_step_size
512

 

한 번의 ECC 스텝당 coreection 가능한 비트 수

# cat ecc_strength
8

 

한 번의 ECC 스텝에서 사용할 OOB 사이즈

# cat oobsize
216

 

그 외 offset 및 size에 시작 위치와 사이즈가 담긴다.

 

NAND 드라이버

Broadcom iproc-nand driver

drivers/mtd/nand/brcmnand/iproc_nand.c

static const struct of_device_id iproc_nand_of_match[] = {
        { .compatible = "brcm,nand-iproc" },
        {},
};
MODULE_DEVICE_TABLE(of, iproc_nand_of_match);

static struct platform_driver iproc_nand_driver = {
        .probe                  = iproc_nand_probe,
        .remove                 = brcmnand_remove,
        .driver = {
                .name           = "iproc_nand",
                .pm             = &brcmnand_pm_ops,
                .of_match_table = iproc_nand_of_match,
        }
};
module_platform_driver(iproc_nand_driver);

이 플랫폼 드라이버에서는 Device Tree의 “brcm,nand-iproc” compatible 명으로 등록된 nand 드라이버의 probe 함수인 iproc_nand_probe()을 호출한다.

 

iproc_nand_probe()

drivers/mtd/nand/brcmnand/iproc_nand.c

static int iproc_nand_probe(struct platform_device *pdev)
{
        struct device *dev = &pdev->dev;
        struct iproc_nand_soc *priv;
        struct brcmnand_soc *soc;
        struct resource *res;

        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
        soc = &priv->soc;

        spin_lock_init(&priv->idm_lock);

        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iproc-idm");
        priv->idm_base = devm_ioremap_resource(dev, res);
        if (IS_ERR(priv->idm_base))
                return PTR_ERR(priv->idm_base);

        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iproc-ext");
        priv->ext_base = devm_ioremap_resource(dev, res);
        if (IS_ERR(priv->ext_base))
                return PTR_ERR(priv->ext_base);

        soc->ctlrdy_ack = iproc_nand_intc_ack;
        soc->ctlrdy_set_enabled = iproc_nand_intc_set;
        soc->prepare_data_bus = iproc_nand_apb_access;

        return brcmnand_probe(pdev, soc);
}

iproc 낸드 드라이버를 probe한다.

  • 코드 라인 8~10에서 iproc 낸드 드라이버 정보를 담을 구조체를 할당받는다.
  • 코드 라인 15~18에서 플랫폼 리소스에 “iproc-idm” 리소스가 있는 경우 idm 레지스터들 물리 주소 범위를 가상 주소에 매핑한다.
    • 예) reg = <0x66460000 0x600>, <0x67015408 0x600>, <0x66460f00 0x20>;
    • reg-names = “nand”, “iproc-idm”, “iproc-ext”;
  • 코드 라인 20~23에서 플랫폼 리소스에 “iproc-ext” 리소스가 있는 경우 ext 레지스터들 물리 주소 범위를 가상 주소에 매핑한다.
  • 코드 라인 25~29에 낸드 인터럽트 처리 및 ARM APB 버스 접근관련 후크 함수를 준비하고 broadcom의 nand probe 함수를 호출한다.

 

아래 broadcom nand 드라이버 다음 버전들을 지원한다.

static const struct of_device_id brcmnand_of_match[] = {
        { .compatible = "brcm,brcmnand-v4.0" },
        { .compatible = "brcm,brcmnand-v5.0" },
        { .compatible = "brcm,brcmnand-v6.0" },
        { .compatible = "brcm,brcmnand-v6.1" },
        { .compatible = "brcm,brcmnand-v6.2" },
        { .compatible = "brcm,brcmnand-v7.0" },
        { .compatible = "brcm,brcmnand-v7.1" },
        { .compatible = "brcm,brcmnand-v7.2" },
        {},
};
MODULE_DEVICE_TABLE(of, brcmnand_of_match);

 

brcmnand_probe()

drivers/mtd/nand/brcmnand/brcmnand.c -1/3-

int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
{
        struct device *dev = &pdev->dev;
        struct device_node *dn = dev->of_node, *child;
        struct brcmnand_controller *ctrl;
        struct resource *res;
        int ret;

        /* We only support device-tree instantiation */
        if (!dn)
                return -ENODEV;

        if (!of_match_node(brcmnand_of_match, dn))
                return -ENODEV;

        ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
        if (!ctrl)
                return -ENOMEM;

        dev_set_drvdata(dev, ctrl);
        ctrl->dev = dev;

        init_completion(&ctrl->done);
        init_completion(&ctrl->dma_done);
        nand_hw_control_init(&ctrl->controller);
        INIT_LIST_HEAD(&ctrl->host_list);

        /* NAND register range */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        ctrl->nand_base = devm_ioremap_resource(dev, res);
        if (IS_ERR(ctrl->nand_base))
                return PTR_ERR(ctrl->nand_base);

        /* Enable clock before using NAND registers */
        ctrl->clk = devm_clk_get(dev, "nand");
        if (!IS_ERR(ctrl->clk)) {
                ret = clk_prepare_enable(ctrl->clk);
                if (ret)
                        return ret;
        } else {
                ret = PTR_ERR(ctrl->clk);
                if (ret == -EPROBE_DEFER)
                        return ret;

                ctrl->clk = NULL;
        }

        /* Initialize NAND revision */
        ret = brcmnand_revision_init(ctrl);
        if (ret)
                goto err;

        /*
         * Most chips have this cache at a fixed offset within 'nand' block.
         * Some must specify this region separately.
         */
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-cache");
        if (res) {
                ctrl->nand_fc = devm_ioremap_resource(dev, res);
                if (IS_ERR(ctrl->nand_fc)) {
                        ret = PTR_ERR(ctrl->nand_fc);
                        goto err;
                }
        } else {
                ctrl->nand_fc = ctrl->nand_base +
                                ctrl->reg_offsets[BRCMNAND_FC_BASE];
        }

broadcom 낸드 드라이버를 probe한다.

  • 코드 라인 10~11에서 디바이스 트리 노드가 없는 경우 지원하지 않는다.
  • 코드 라인 13~14에서 매치되는 nand 디바이스 버전을 체크한다.
  • 코드 라인 16~18에서 broadcom 낸드 컨트롤러 구조체를 할당한다.
  • 코드 라인 29~32에서 NAND 레지스터 정보가 담긴 플랫폼 리소스를 얻어와서 가상 주소에 매핑한다.
  • 코드 라인 35~46에서 디바이스 트리에 “nand” 클럭 정보가 있는 경우 클럭을 준비하고 없으면 null 인채로 진행한다.
  • 코드 라인 49~51에서 broadcom 낸드 컨트롤러 버전에 맞는 정보를 알아온다.
    • reg_offsets
      • 레지스터 offset
    • reg_spacing
      • 칩셀렉트 stride
    • cs_offsets
      • 칩 셀렉트 offset
    • max_page_size
      • 컨트롤러가 처리할 수 있는 최대 페이지 사이즈
    • max_block_size
      • 컨트롤러가 처리할 수 있는 최대 블럭 사이즈
    • cs_offsets & cs0_offsets
      • 칩 셀렉트 레지스터 offset
    • max_oob
      • 컨트롤러가 처리할 수 있는 최대 OOB 사이즈
    • features
      • BRCMNAND_HAS_1K_SECTORS
        • ECC의 1K 섹터 지원 여부
        • v5.0 이상(v6.1은 제외)
      • BRCMNAND_HAS_PREFETCH
        • prefetch 지원 여부
        • v6.0 이상(v6.1은 제외)
      • BRCMNAND_HAS_CACHE_MODE
        • cache 지원 여부
        • v7.0 이상
      • BRCMNAND_HAS_WP
        • write protect 지원 여부
        • v7.0 이상 또는 “brcm,nand-has-wp” 속성이 있는 경우
  • 코드 라인 57~67에서 디바이스 트리에 “nand-cache”에 대한 레지스터 범위가 있는 경우 이를 가상 주소에 매핑한다.

 

drivers/mtd/nand/brcmnand/brcmnand.c -2/3-

.       /* FLASH_DMA */
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "flash-dma");
        if (res) {
                ctrl->flash_dma_base = devm_ioremap_resource(dev, res);
                if (IS_ERR(ctrl->flash_dma_base)) {
                        ret = PTR_ERR(ctrl->flash_dma_base);
                        goto err;
                }

                flash_dma_writel(ctrl, FLASH_DMA_MODE, 1); /* linked-list */
                flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0);

                /* Allocate descriptor(s) */
                ctrl->dma_desc = dmam_alloc_coherent(dev,
                                                     sizeof(*ctrl->dma_desc),
                                                     &ctrl->dma_pa, GFP_KERNEL);
                if (!ctrl->dma_desc) {
                        ret = -ENOMEM;
                        goto err;
                }

                ctrl->dma_irq = platform_get_irq(pdev, 1);
                if ((int)ctrl->dma_irq < 0) {
                        dev_err(dev, "missing FLASH_DMA IRQ\n");
                        ret = -ENODEV;
                        goto err;
                }

                ret = devm_request_irq(dev, ctrl->dma_irq,
                                brcmnand_dma_irq, 0, DRV_NAME,
                                ctrl);
                if (ret < 0) {
                        dev_err(dev, "can't allocate IRQ %d: error %d\n",
                                        ctrl->dma_irq, ret);
                        goto err;
                }

                dev_info(dev, "enabling FLASH_DMA\n");
        }

        /* Disable automatic device ID config, direct addressing */
        brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT,
                         CS_SELECT_AUTO_DEVICE_ID_CFG | 0xff, 0, 0);
        /* Disable XOR addressing */
        brcmnand_rmw_reg(ctrl, BRCMNAND_CS_XOR, 0xff, 0, 0);

        if (ctrl->features & BRCMNAND_HAS_WP) {
                /* Permanently disable write protection */
                if (wp_on == 2)
                        brcmnand_set_wp(ctrl, false);
        } else {
                wp_on = 0;
        }

        /* IRQ */
        ctrl->irq = platform_get_irq(pdev, 0);
        if ((int)ctrl->irq < 0) {
                dev_err(dev, "no IRQ defined\n");
                ret = -ENODEV;
                goto err;
        }
  • 코드 라인 2~39에서 디바이스 트리에서 “flash-dma” 속성이 있는 경우 dma를 준비한다. 이 때 dma에 사용할 인터럽트와 핸들러를 준비한다.
    • 디바이스 트리에서 인터럽트는 두 번째 항목에 있는 것을 사용한다.
  • 코드 라인 42~43에서 기본 설정으로 낸드 칩을 선택하지 않고 disable 상태로 둔다.
  • 코드 라인 45에서 XOR 어드레싱을 disable한다.
  • 코드 라인 47~53에서 write protect 기능이 있고 모듈 파라메터 “wp_on”의 값이 2인 경우 write protection 처리한다.
  • 코드 라인 56~61에서 NAND 컨트롤러에서 사용할 인터럽트를 준비한다.

 

drivers/mtd/nand/brcmnand/brcmnand.c -3/3-

        /*
         * Some SoCs integrate this controller (e.g., its interrupt bits) in
         * interesting ways
         */
        if (soc) {
                ctrl->soc = soc;

                ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0,
                                       DRV_NAME, ctrl);

                /* Enable interrupt */
                ctrl->soc->ctlrdy_ack(ctrl->soc);
                ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
        } else {
                /* Use standard interrupt infrastructure */
                ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0,
                                       DRV_NAME, ctrl);
        }
        if (ret < 0) {
                dev_err(dev, "can't allocate IRQ %d: error %d\n",
                        ctrl->irq, ret);
                goto err;
        }

        for_each_available_child_of_node(dn, child) {
                if (of_device_is_compatible(child, "brcm,nandcs")) {
                        struct brcmnand_host *host;

                        host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
                        if (!host) {
                                of_node_put(child);
                                ret = -ENOMEM;
                                goto err;
                        }
                        host->pdev = pdev;
                        host->ctrl = ctrl;

                        ret = brcmnand_init_cs(host, child);
                        if (ret) {
                                devm_kfree(dev, host);
                                continue; /* Try all chip-selects */
                        }

                        list_add_tail(&host->node, &ctrl->host_list);
                }
        }

        /* No chip-selects could initialize properly */
        if (list_empty(&ctrl->host_list)) {
                ret = -ENODEV;
                goto err;
        }

        return 0;

err:
        clk_disable_unprepare(ctrl->clk);
        return ret;

}
EXPORT_SYMBOL_GPL(brcmnand_probe);
  • 코드 라인 5~23에서 broadcom nand driver가 독립된 표준 모드로 동작하거나 iproc과 같이 인터럽트를 enable하기 위해 추가로 후크 함수들을 준비하는 soc 모드 등 두 가지 모드가 있다.
    • 표준 모드
      • brcmnand_ctlrdy_irq() 인터럽트 핸들러를 사용하도록 준비한다.
    • soc 모드
      • brcmnand_irq() 인터럽트 핸들러를 사용하도록 준비한다.
      • 인터럽트를 enable하기 위해 해당 soc에서 준비한 후크 함수들을 호출한다.
  • 코드 라인 25~46에서 nand 노드의 하위 노드에 “brcm,nandcs” compatible 명이 있는 경우 해당 칩셀렉트 번호에 해당하는 NAND 호스트 컨트롤러의 operation과 ecc에 대한 operation 후크 함수들을 준비하고 nand 컨트롤러를 설정한 후 mtd 디바이스로 등록한다.
  • 코드 라인 49~54에서 등록된 NAND 호스트 컨트롤러가 없는 경우 함수를 빠져나간다.

 

brcmnand_init_cs()

drivers/mtd/nand/brcmnand/brcmnand.c -1/2-

static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
{
        struct brcmnand_controller *ctrl = host->ctrl;
        struct platform_device *pdev = host->pdev;
        struct mtd_info *mtd;
        struct nand_chip *chip;
        int ret;
        u16 cfg_offs;

        ret = of_property_read_u32(dn, "reg", &host->cs);
        if (ret) {
                dev_err(&pdev->dev, "can't get chip-select\n");
                return -ENXIO;
        }

        mtd = nand_to_mtd(&host->chip);
        chip = &host->chip;

        nand_set_flash_node(chip, dn);
        nand_set_controller_data(chip, host);
        mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "brcmnand.%d",
                                   host->cs);
        mtd->owner = THIS_MODULE;
        mtd->dev.parent = &pdev->dev;

        chip->IO_ADDR_R = (void __iomem *)0xdeadbeef;
        chip->IO_ADDR_W = (void __iomem *)0xdeadbeef;

        chip->cmd_ctrl = brcmnand_cmd_ctrl;
        chip->cmdfunc = brcmnand_cmdfunc;
        chip->waitfunc = brcmnand_waitfunc;
        chip->read_byte = brcmnand_read_byte;
        chip->read_buf = brcmnand_read_buf;
        chip->write_buf = brcmnand_write_buf;

        chip->ecc.mode = NAND_ECC_HW;
        chip->ecc.read_page = brcmnand_read_page;
        chip->ecc.write_page = brcmnand_write_page;
        chip->ecc.read_page_raw = brcmnand_read_page_raw;
        chip->ecc.write_page_raw = brcmnand_write_page_raw;
        chip->ecc.write_oob_raw = brcmnand_write_oob_raw;
        chip->ecc.read_oob_raw = brcmnand_read_oob_raw;
        chip->ecc.read_oob = brcmnand_read_oob;
        chip->ecc.write_oob = brcmnand_write_oob;

        chip->controller = &ctrl->controller;
  • 코드 라인 10~14에서 nand 노드에서 reg 속성값을 읽어온다.
  • 코드 라인 16~46에서 nand chip 드라이버에서 사용할 멤버들을 초기화하고, 호출될 후크 함수들을 준비한다.

 

drivers/mtd/nand/brcmnand/brcmnand.c -2/2-

        /*
         * The bootloader might have configured 16bit mode but
         * NAND READID command only works in 8bit mode. We force
         * 8bit mode here to ensure that NAND READID commands works.
         */
        cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG);
        nand_writereg(ctrl, cfg_offs,
                      nand_readreg(ctrl, cfg_offs) & ~CFG_BUS_WIDTH);

        ret = nand_scan_ident(mtd, 1, NULL);
        if (ret)
                return ret;

        chip->options |= NAND_NO_SUBPAGE_WRITE;
        /*
         * Avoid (for instance) kmap()'d buffers from JFFS2, which we can't DMA
         * to/from, and have nand_base pass us a bounce buffer instead, as
         * needed.
         */
        chip->options |= NAND_USE_BOUNCE_BUFFER;

        if (chip->bbt_options & NAND_BBT_USE_FLASH)
                chip->bbt_options |= NAND_BBT_NO_OOB;

        if (brcmnand_setup_dev(host))
                return -ENXIO;

        chip->ecc.size = host->hwcfg.sector_size_1k ? 1024 : 512;
        /* only use our internal HW threshold */
        mtd->bitflip_threshold = 1;

        ret = brcmstb_choose_ecc_layout(host);
        if (ret)
                return ret;

        ret = nand_scan_tail(mtd);
        if (ret)
                return ret;

        return mtd_device_register(mtd, NULL, 0);
}
  • 코드 라인 6~8에서 부트로더에서 16bit 모드로 설정되어 있을 수도 있다. NAND READID 명령은 8bit 모드에서 동작해야 하므로 8비트 모드로 변경한다.
  • 코드 라인 10~12에서 nand 디바이스를 스캔하는 1st phase이다.
  • 코드 라인 14~26에서 broadcom nand 호스트 컨트롤러를 설정한다.
  • 코드 라인 28~34에서 ecc가 저장되는 oob layout을 결정한다.
  • 코드 라인 36~38에서 nand 디바이스를 스캔하는 2nd phase이다.
  • 코드 라인 40에서 mtd 디바이스로 등록한다.

 

다음 그림은 broadcom사의 nand_chip 구조체에 설정된 후크함수들을 보여준다.

 

참고

 

댓글 남기기