<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 바이트)
- 하위 노드인 파티션 노드의 주소 표현에 필요한 4바이트 수
- #size-cells
- 하위 노드인 파티션 노드의 사이즈 표현에 필요한 4바이트 수
- 1 = 32bit 주소(4 바이트)
- 2 = 64bit 주소(8 바이트)
- 하위 노드인 파티션 노드의 사이즈 표현에 필요한 4바이트 수
- nand-on-flash-bbt
- NAND에 BBT(Bad Block Table)를 지원하는지 여부
- “true” or “false”
- NAND에 BBT(Bad Block Table)를 지원하는지 여부
- 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 모드 문자열로 다음과 같다.
- nand-ecc-algo
- NAND ECC 알고리즘 문자열로 다음과 같다.
- “hamming”
- “bch”
- NAND ECC 알고리즘 문자열로 다음과 같다.
- nand-bus-width
- NAND 데이터 버스 사이즈
- 8 = x8
- 16 = x16
- NAND 데이터 버스 사이즈
- nand-ecc-strength
- ECC 한 스텝당 correction 가능한 비트 수
- 1, 5, 6, 8, 10, 12, 14, 16, …
- nand-ecc-step-size
- ECC 한 스텝 사이즈
- 512 또는 1024
- ECC 한 스텝 사이즈
- 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, …
- OOB 사이즈
- 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” 속성이 있는 경우
- BRCMNAND_HAS_1K_SECTORS
- reg_offsets
- 코드 라인 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 구조체에 설정된 후크함수들을 보여준다.
참고
- MTD(Memory Technology Device) -1-
- MTD(Memory Technology Device) -2- | 현재 글
- MTD(Memory Technology Device) -3-