Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mtd: nand: Add OX820 NAND Support

Add NAND driver to support the Oxford Semiconductor OX820 NAND Controller.
This is a simple memory mapped NAND controller with single chip select and
software ECC.

Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>

authored by

Neil Armstrong and committed by
Boris Brezillon
66859249 6ac18a48

+242
+41
Documentation/devicetree/bindings/mtd/oxnas-nand.txt
··· 1 + * Oxford Semiconductor OXNAS NAND Controller 2 + 3 + Please refer to nand.txt for generic information regarding MTD NAND bindings. 4 + 5 + Required properties: 6 + - compatible: "oxsemi,ox820-nand" 7 + - reg: Base address and length for NAND mapped memory. 8 + 9 + Optional Properties: 10 + - clocks: phandle to the NAND gate clock if needed. 11 + - resets: phandle to the NAND reset control if needed. 12 + 13 + Example: 14 + 15 + nandc: nand-controller@41000000 { 16 + compatible = "oxsemi,ox820-nand"; 17 + reg = <0x41000000 0x100000>; 18 + clocks = <&stdclk CLK_820_NAND>; 19 + resets = <&reset RESET_NAND>; 20 + #address-cells = <1>; 21 + #size-cells = <0>; 22 + 23 + nand@0 { 24 + reg = <0>; 25 + #address-cells = <1>; 26 + #size-cells = <1>; 27 + nand-ecc-mode = "soft"; 28 + nand-ecc-algo = "hamming"; 29 + 30 + partition@0 { 31 + label = "boot"; 32 + reg = <0x00000000 0x00e00000>; 33 + read-only; 34 + }; 35 + 36 + partition@e00000 { 37 + label = "ubi"; 38 + reg = <0x00e00000 0x07200000>; 39 + }; 40 + }; 41 + };
+5
drivers/mtd/nand/Kconfig
··· 426 426 No board specific support is done by this driver, each board 427 427 must advertise a platform_device for the driver to attach. 428 428 429 + config MTD_NAND_OXNAS 430 + tristate "NAND Flash support for Oxford Semiconductor SoC" 431 + help 432 + This enables the NAND flash controller on Oxford Semiconductor SoCs. 433 + 429 434 config MTD_NAND_FSL_ELBC 430 435 tristate "NAND support for Freescale eLBC controllers" 431 436 depends on FSL_SOC
+1
drivers/mtd/nand/Makefile
··· 35 35 obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o 36 36 obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o 37 37 obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o 38 + obj-$(CONFIG_MTD_NAND_OXNAS) += oxnas_nand.o 38 39 obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o 39 40 obj-$(CONFIG_MTD_NAND_FSL_IFC) += fsl_ifc_nand.o 40 41 obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o
+195
drivers/mtd/nand/oxnas_nand.c
··· 1 + /* 2 + * Oxford Semiconductor OXNAS NAND driver 3 + 4 + * Copyright (C) 2016 Neil Armstrong <narmstrong@baylibre.com> 5 + * Heavily based on plat_nand.c : 6 + * Author: Vitaly Wool <vitalywool@gmail.com> 7 + * Copyright (C) 2013 Ma Haijun <mahaijuns@gmail.com> 8 + * Copyright (C) 2012 John Crispin <blogic@openwrt.org> 9 + * 10 + * This program is free software; you can redistribute it and/or modify 11 + * it under the terms of the GNU General Public License version 2 as 12 + * published by the Free Software Foundation. 13 + * 14 + */ 15 + 16 + #include <linux/err.h> 17 + #include <linux/io.h> 18 + #include <linux/module.h> 19 + #include <linux/platform_device.h> 20 + #include <linux/slab.h> 21 + #include <linux/clk.h> 22 + #include <linux/reset.h> 23 + #include <linux/mtd/mtd.h> 24 + #include <linux/mtd/nand.h> 25 + #include <linux/mtd/partitions.h> 26 + #include <linux/of.h> 27 + 28 + /* Nand commands */ 29 + #define OXNAS_NAND_CMD_ALE BIT(18) 30 + #define OXNAS_NAND_CMD_CLE BIT(19) 31 + 32 + #define OXNAS_NAND_MAX_CHIPS 1 33 + 34 + struct oxnas_nand_ctrl { 35 + struct nand_hw_control base; 36 + void __iomem *io_base; 37 + struct clk *clk; 38 + struct nand_chip *chips[OXNAS_NAND_MAX_CHIPS]; 39 + }; 40 + 41 + static uint8_t oxnas_nand_read_byte(struct mtd_info *mtd) 42 + { 43 + struct nand_chip *chip = mtd_to_nand(mtd); 44 + struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip); 45 + 46 + return readb(oxnas->io_base); 47 + } 48 + 49 + static void oxnas_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len) 50 + { 51 + struct nand_chip *chip = mtd_to_nand(mtd); 52 + struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip); 53 + 54 + ioread8_rep(oxnas->io_base, buf, len); 55 + } 56 + 57 + static void oxnas_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len) 58 + { 59 + struct nand_chip *chip = mtd_to_nand(mtd); 60 + struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip); 61 + 62 + iowrite8_rep(oxnas->io_base, buf, len); 63 + } 64 + 65 + /* Single CS command control */ 66 + static void oxnas_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, 67 + unsigned int ctrl) 68 + { 69 + struct nand_chip *chip = mtd_to_nand(mtd); 70 + struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip); 71 + 72 + if (ctrl & NAND_CLE) 73 + writeb(cmd, oxnas->io_base + OXNAS_NAND_CMD_CLE); 74 + else if (ctrl & NAND_ALE) 75 + writeb(cmd, oxnas->io_base + OXNAS_NAND_CMD_ALE); 76 + } 77 + 78 + /* 79 + * Probe for the NAND device. 80 + */ 81 + static int oxnas_nand_probe(struct platform_device *pdev) 82 + { 83 + struct device_node *np = pdev->dev.of_node; 84 + struct device_node *nand_np; 85 + struct oxnas_nand_ctrl *oxnas; 86 + struct nand_chip *chip; 87 + struct mtd_info *mtd; 88 + struct resource *res; 89 + int nchips = 0; 90 + int count = 0; 91 + int err = 0; 92 + 93 + /* Allocate memory for the device structure (and zero it) */ 94 + oxnas = devm_kzalloc(&pdev->dev, sizeof(struct nand_chip), 95 + GFP_KERNEL); 96 + if (!oxnas) 97 + return -ENOMEM; 98 + 99 + nand_hw_control_init(&oxnas->base); 100 + 101 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 102 + oxnas->io_base = devm_ioremap_resource(&pdev->dev, res); 103 + if (IS_ERR(oxnas->io_base)) 104 + return PTR_ERR(oxnas->io_base); 105 + 106 + oxnas->clk = devm_clk_get(&pdev->dev, NULL); 107 + if (IS_ERR(oxnas->clk)) 108 + oxnas->clk = NULL; 109 + 110 + /* Only a single chip node is supported */ 111 + count = of_get_child_count(np); 112 + if (count > 1) 113 + return -EINVAL; 114 + 115 + clk_prepare_enable(oxnas->clk); 116 + device_reset_optional(&pdev->dev); 117 + 118 + for_each_child_of_node(np, nand_np) { 119 + chip = devm_kzalloc(&pdev->dev, sizeof(struct nand_chip), 120 + GFP_KERNEL); 121 + if (!chip) 122 + return -ENOMEM; 123 + 124 + chip->controller = &oxnas->base; 125 + 126 + nand_set_flash_node(chip, nand_np); 127 + nand_set_controller_data(chip, oxnas); 128 + 129 + mtd = nand_to_mtd(chip); 130 + mtd->dev.parent = &pdev->dev; 131 + mtd->priv = chip; 132 + 133 + chip->cmd_ctrl = oxnas_nand_cmd_ctrl; 134 + chip->read_buf = oxnas_nand_read_buf; 135 + chip->read_byte = oxnas_nand_read_byte; 136 + chip->write_buf = oxnas_nand_write_buf; 137 + chip->chip_delay = 30; 138 + 139 + /* Scan to find existence of the device */ 140 + err = nand_scan(mtd, 1); 141 + if (err) 142 + return err; 143 + 144 + err = mtd_device_register(mtd, NULL, 0); 145 + if (err) { 146 + nand_release(mtd); 147 + return err; 148 + } 149 + 150 + oxnas->chips[nchips] = chip; 151 + ++nchips; 152 + } 153 + 154 + /* Exit if no chips found */ 155 + if (!nchips) 156 + return -ENODEV; 157 + 158 + platform_set_drvdata(pdev, oxnas); 159 + 160 + return 0; 161 + } 162 + 163 + static int oxnas_nand_remove(struct platform_device *pdev) 164 + { 165 + struct oxnas_nand_ctrl *oxnas = platform_get_drvdata(pdev); 166 + 167 + if (oxnas->chips[0]) 168 + nand_release(nand_to_mtd(oxnas->chips[0])); 169 + 170 + clk_disable_unprepare(oxnas->clk); 171 + 172 + return 0; 173 + } 174 + 175 + static const struct of_device_id oxnas_nand_match[] = { 176 + { .compatible = "oxsemi,ox820-nand" }, 177 + {}, 178 + }; 179 + MODULE_DEVICE_TABLE(of, oxnas_nand_match); 180 + 181 + static struct platform_driver oxnas_nand_driver = { 182 + .probe = oxnas_nand_probe, 183 + .remove = oxnas_nand_remove, 184 + .driver = { 185 + .name = "oxnas_nand", 186 + .of_match_table = oxnas_nand_match, 187 + }, 188 + }; 189 + 190 + module_platform_driver(oxnas_nand_driver); 191 + 192 + MODULE_LICENSE("GPL"); 193 + MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 194 + MODULE_DESCRIPTION("Oxnas NAND driver"); 195 + MODULE_ALIAS("platform:oxnas_nand");