From 5cde94d9ab577c5ab68fc71e15a05d1bda5041f2 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Sat, 24 Sep 2016 01:14:53 +0200 Subject: oxnas: backport upstream NAND driver Signed-off-by: Daniel Golle --- .../oxnas/files/arch/arm/boot/dts/ox820-akitio.dts | 30 ++- .../oxnas/files/arch/arm/boot/dts/ox820-kd20.dts | 70 +++--- .../files/arch/arm/boot/dts/ox820-pogoplug-pro.dts | 38 ++-- .../files/arch/arm/boot/dts/ox820-pogoplug-v3.dts | 38 ++-- .../oxnas/files/arch/arm/boot/dts/ox820-stg212.dts | 37 ++-- .../linux/oxnas/files/arch/arm/boot/dts/ox820.dtsi | 9 +- .../oxnas/files/drivers/mtd/nand/oxnas_nand.c | 243 +++++++++++++++------ 7 files changed, 313 insertions(+), 152 deletions(-) (limited to 'target/linux/oxnas/files') diff --git a/target/linux/oxnas/files/arch/arm/boot/dts/ox820-akitio.dts b/target/linux/oxnas/files/arch/arm/boot/dts/ox820-akitio.dts index 712c03e..5f7c2a1 100644 --- a/target/linux/oxnas/files/arch/arm/boot/dts/ox820-akitio.dts +++ b/target/linux/oxnas/files/arch/arm/boot/dts/ox820-akitio.dts @@ -34,15 +34,6 @@ nand@41000000 { status = "okay"; - partition@0 { - label = "boot"; - reg = <0x0 0x26c0000>; - }; - - partition@26c0000 { - label = "ubi"; - reg = <0x26c0000 0xd940000>; - }; }; ethernet@40400000 { @@ -132,3 +123,24 @@ }; }; }; + +&nandc { + status = "okay"; + + nand@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + nand-ecc-mode = "soft"; + nand-ecc-algo = "hamming"; + + partition@0 { + label = "boot"; + reg = <0x00000000 0x026c0000>; + }; + + partition@26c0000 { + label = "ubi"; + reg = <0x026c0000 0x0d940000>; + }; +}; diff --git a/target/linux/oxnas/files/arch/arm/boot/dts/ox820-kd20.dts b/target/linux/oxnas/files/arch/arm/boot/dts/ox820-kd20.dts index 1312fd6..a59addc 100644 --- a/target/linux/oxnas/files/arch/arm/boot/dts/ox820-kd20.dts +++ b/target/linux/oxnas/files/arch/arm/boot/dts/ox820-kd20.dts @@ -31,37 +31,6 @@ nr-ports = <2>; }; - nand@41000000 { - status = "okay"; - - partition@0 { - label = "stage1"; - reg = <0x00000000 0x00040000>; - read-only; - }; - - partition@40000 { - label = "u-boot"; - reg = <0x00040000 0x00200000>; - read-only; - }; - - partition@240000 { - label = "initrd"; - reg = <0x00240000 0x00600000>; - }; - - partition@840000 { - label = "kernel"; - reg = <0x00840000 0x007C0000>; - }; - - partition@e00000 { - label = "ubi"; - reg = <0x01000000 0x07000000>; - }; - }; - ethernet@40400000 { status = "okay"; snps,phy-addr = <1>; @@ -163,3 +132,42 @@ gpios = <&GPIOA 9 0>; }; }; + +&nandc { + status = "okay"; + + nand@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + nand-ecc-mode = "soft"; + nand-ecc-algo = "hamming"; + + partition@0 { + label = "stage1"; + reg = <0x00000000 0x00040000>; + read-only; + }; + + partition@40000 { + label = "u-boot"; + reg = <0x00040000 0x00200000>; + read-only; + }; + + partition@240000 { + label = "initrd"; + reg = <0x00240000 0x00600000>; + }; + + partition@840000 { + label = "kernel"; + reg = <0x00840000 0x007C0000>; + }; + + partition@e00000 { + label = "ubi"; + reg = <0x01000000 0x07000000>; + }; + }; +}; diff --git a/target/linux/oxnas/files/arch/arm/boot/dts/ox820-pogoplug-pro.dts b/target/linux/oxnas/files/arch/arm/boot/dts/ox820-pogoplug-pro.dts index f3730c3..5b087e9 100644 --- a/target/linux/oxnas/files/arch/arm/boot/dts/ox820-pogoplug-pro.dts +++ b/target/linux/oxnas/files/arch/arm/boot/dts/ox820-pogoplug-pro.dts @@ -28,21 +28,6 @@ status = "okay"; }; - nand@41000000 { - status = "okay"; - - partition@0 { - label = "boot"; - reg = <0x00000000 0x00e00000>; - /*read-only;*/ - }; - - partition@e00000 { - label = "ubi"; - reg = <0x00e00000 0x07200000>; - }; - }; - ethernet@40400000 { status = "okay"; }; @@ -84,3 +69,26 @@ }; }; }; + +&nandc { + status = "okay"; + + nand@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + nand-ecc-mode = "soft"; + nand-ecc-algo = "hamming"; + + partition@0 { + label = "boot"; + reg = <0x00000000 0x00e00000>; + /*read-only;*/ + }; + + partition@e00000 { + label = "ubi"; + reg = <0x00e00000 0x07200000>; + }; + }; +}; diff --git a/target/linux/oxnas/files/arch/arm/boot/dts/ox820-pogoplug-v3.dts b/target/linux/oxnas/files/arch/arm/boot/dts/ox820-pogoplug-v3.dts index 8029132..be0f6c9 100644 --- a/target/linux/oxnas/files/arch/arm/boot/dts/ox820-pogoplug-v3.dts +++ b/target/linux/oxnas/files/arch/arm/boot/dts/ox820-pogoplug-v3.dts @@ -25,21 +25,6 @@ status = "okay"; }; - nand@41000000 { - status = "okay"; - - partition@0 { - label = "boot"; - reg = <0x00000000 0x00e00000>; - /*read-only;*/ - }; - - partition@e00000 { - label = "ubi"; - reg = <0x00e00000 0x07200000>; - }; - }; - ethernet@40400000 { status = "okay"; }; @@ -81,3 +66,26 @@ }; }; + +&nandc { + status = "okay"; + + nand@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + nand-ecc-mode = "soft"; + nand-ecc-algo = "hamming"; + + partition@0 { + label = "boot"; + reg = <0x00000000 0x00e00000>; + /*read-only;*/ + }; + + partition@e00000 { + label = "ubi"; + reg = <0x00e00000 0x07200000>; + }; + }; +}; diff --git a/target/linux/oxnas/files/arch/arm/boot/dts/ox820-stg212.dts b/target/linux/oxnas/files/arch/arm/boot/dts/ox820-stg212.dts index a8112c9..ad93d4e 100644 --- a/target/linux/oxnas/files/arch/arm/boot/dts/ox820-stg212.dts +++ b/target/linux/oxnas/files/arch/arm/boot/dts/ox820-stg212.dts @@ -27,20 +27,6 @@ status = "okay"; }; - nand@41000000 { - status = "okay"; - - partition@0 { - label = "boot"; - reg = <0x00000000 0x00e00000>; - /*read-only;*/ - }; - - partition@e00000 { - label = "ubi"; - reg = <0x00e00000 0x07200000>; - }; - }; ethernet@40400000 { status = "okay"; @@ -91,3 +77,26 @@ }; }; + +&nandc { + status = "okay"; + + nand@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + nand-ecc-mode = "soft"; + nand-ecc-algo = "hamming"; + + partition@0 { + label = "boot"; + reg = <0x00000000 0x00e00000>; + read-only; + }; + + partition@e00000 { + label = "ubi"; + reg = <0x00e00000 0x07200000>; + }; + }; +}; diff --git a/target/linux/oxnas/files/arch/arm/boot/dts/ox820.dtsi b/target/linux/oxnas/files/arch/arm/boot/dts/ox820.dtsi index 4ba4b8b..c096a7d 100644 --- a/target/linux/oxnas/files/arch/arm/boot/dts/ox820.dtsi +++ b/target/linux/oxnas/files/arch/arm/boot/dts/ox820.dtsi @@ -298,16 +298,15 @@ status = "disabled"; }; - nand@41000000 { - compatible = "plxtech,nand-nas782x", "gen_nand"; - reg = <0x41000000 0x100000>, <0x41C00000 0x20>; - nand-ecc-mode = "soft"; + nandc: nand-controller@41000000 { + compatible = "oxsemi,ox820-nand"; + reg = <0x41000000 0x100000>; clocks = <&stdclk 9>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_nand0>; resets = <&rst 15>; #address-cells = <1>; - #size-cells = <1>; + #size-cells = <0>; status = "disabled"; }; diff --git a/target/linux/oxnas/files/drivers/mtd/nand/oxnas_nand.c b/target/linux/oxnas/files/drivers/mtd/nand/oxnas_nand.c index 9536e51..6cb17cd 100644 --- a/target/linux/oxnas/files/drivers/mtd/nand/oxnas_nand.c +++ b/target/linux/oxnas/files/drivers/mtd/nand/oxnas_nand.c @@ -1,94 +1,211 @@ /* - * 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. + * Oxford Semiconductor OXNAS NAND driver + + * Copyright (C) 2016 Neil Armstrong + * Heavily based on plat_nand.c : + * Author: Vitaly Wool + * Copyright (C) 2013 Ma Haijun + * Copyright (C) 2012 John Crispin + * + * 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. * - * based on xway_nand.c - * Copyright © 2012 John Crispin - * and oxnas_nand.c "NAND glue for Oxnas platforms" - * written by Ma Haijun */ -#include -#include -#include +#include +#include +#include +#include +#include #include #include +#include +#include +#include +#include + +/* Nand commands */ +#define OXNAS_NAND_CMD_ALE BIT(18) +#define OXNAS_NAND_CMD_CLE BIT(19) + +#define OXNAS_NAND_MAX_CHIPS 1 + +struct oxnas_nand { + struct nand_hw_control base; + void __iomem *io_base; + struct clk *clk; + struct nand_chip *chips[OXNAS_NAND_MAX_CHIPS]; + unsigned long ctrl; + struct mtd_partition *partitions; + int nr_partitions; +}; + +static uint8_t oxnas_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct oxnas_nand *oxnas = nand_get_controller_data(chip); -/* nand commands */ -#define NAND_CMD_ALE BIT(18) -#define NAND_CMD_CLE BIT(19) -#define NAND_CMD_CS 0 -#define NAND_CMD_RESET 0xff -#define NAND_CMD (NAND_CMD_CS | NAND_CMD_CLE) -#define NAND_ADDR (NAND_CMD_CS | NAND_CMD_ALE) -#define NAND_DATA (NAND_CMD_CS) + return readb(oxnas->io_base); +} -static void oxnas_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +static void oxnas_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) { - struct nand_chip *this = mtd->priv; - unsigned long nandaddr = (unsigned long) this->IO_ADDR_W; + struct nand_chip *chip = mtd_to_nand(mtd); + struct oxnas_nand *oxnas = nand_get_controller_data(chip); + + ioread8_rep(oxnas->io_base, buf, len); +} + +static void oxnas_nand_write_buf(struct mtd_info *mtd, + const uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct oxnas_nand *oxnas = nand_get_controller_data(chip); + + iowrite8_rep(oxnas->io_base + oxnas->ctrl, buf, len); +} + +/* Single CS command control */ +static void oxnas_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct oxnas_nand *oxnas = nand_get_controller_data(chip); if (ctrl & NAND_CTRL_CHANGE) { - nandaddr &= ~(NAND_CMD | NAND_ADDR); if (ctrl & NAND_CLE) - nandaddr |= NAND_CMD; + oxnas->ctrl = OXNAS_NAND_CMD_CLE; else if (ctrl & NAND_ALE) - nandaddr |= NAND_ADDR; - this->IO_ADDR_W = (void __iomem *) nandaddr; + oxnas->ctrl = OXNAS_NAND_CMD_ALE; + else + oxnas->ctrl = 0; } if (cmd != NAND_CMD_NONE) - writeb(cmd, (void __iomem *) nandaddr); + writeb(cmd, oxnas->io_base + oxnas->ctrl); } +/* + * Probe for the NAND device. + */ static int oxnas_nand_probe(struct platform_device *pdev) { - /* enable clock and release static block reset */ - struct clk *clk = of_clk_get(pdev->dev.of_node, 0); + struct device_node *np = pdev->dev.of_node; + struct device_node *nand_np; + struct oxnas_nand *oxnas; + struct nand_chip *chip; + struct mtd_info *mtd; + struct resource *res; + int nchips = 0; + int count = 0; + int err = 0; - if (IS_ERR(clk)) - return PTR_ERR(clk); + /* Allocate memory for the device structure (and zero it) */ + oxnas = devm_kzalloc(&pdev->dev, sizeof(struct nand_chip), + GFP_KERNEL); + if (!oxnas) + return -ENOMEM; - clk_prepare_enable(clk); - device_reset(&pdev->dev); + nand_hw_control_init(&oxnas->base); - return 0; -} + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + oxnas->io_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(oxnas->io_base)) + return PTR_ERR(oxnas->io_base); -/* allow users to override the partition in DT using the cmdline */ -static const char * part_probes[] = { "cmdlinepart", "ofpart", NULL }; + oxnas->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(oxnas->clk)) + oxnas->clk = NULL; -static struct platform_nand_data oxnas_nand_data = { - .chip = { - .nr_chips = 1, - .chip_delay = 30, - .part_probe_types = part_probes, - }, - .ctrl = { - .probe = oxnas_nand_probe, - .cmd_ctrl = oxnas_cmd_ctrl, + /* Only a single chip node is supported */ + count = of_get_child_count(np); + pr_info("########### count: %d\n", count); + if (count > 1) + return -EINVAL; + + clk_prepare_enable(oxnas->clk); + device_reset_optional(&pdev->dev); + + for_each_child_of_node(np, nand_np) { + chip = devm_kzalloc(&pdev->dev, sizeof(struct nand_chip), + GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->controller = &oxnas->base; + + nand_set_flash_node(chip, nand_np); + nand_set_controller_data(chip, oxnas); + + mtd = nand_to_mtd(chip); + mtd->dev.parent = &pdev->dev; + mtd->priv = chip; + + chip->cmd_ctrl = oxnas_nand_cmd_ctrl; + chip->read_buf = oxnas_nand_read_buf; + chip->read_byte = oxnas_nand_read_byte; + chip->write_buf = oxnas_nand_write_buf; + chip->chip_delay = 30; + + /* Scan to find existence of the device */ + err = nand_scan(mtd, 1); + pr_info("########### nand_scan err: %d\n", err); + if (err) + return err; + + err = mtd_device_register(mtd, NULL, 0); + pr_info("########### mtd_device_register err: %d\n", err); + if (err) { + nand_release(mtd); + return err; + } + + oxnas->chips[nchips] = chip; + ++nchips; } -}; -/* - * Try to find the node inside the DT. If it is available attach out - * platform_nand_data - */ -static int __init oxnas_register_nand(void) + pr_info("########### nchips: %d\n", nchips); + + /* Exit if no chips found */ + if (!nchips) + return -ENODEV; + + platform_set_drvdata(pdev, oxnas); + + return 0; +} + +static int oxnas_nand_remove(struct platform_device *pdev) { - struct device_node *node; - struct platform_device *pdev; - - node = of_find_compatible_node(NULL, NULL, "plxtech,nand-nas782x"); - if (!node) - return -ENOENT; - pdev = of_find_device_by_node(node); - if (!pdev) - return -EINVAL; - pdev->dev.platform_data = &oxnas_nand_data; - of_node_put(node); + struct oxnas_nand *oxnas = platform_get_drvdata(pdev); + + if (oxnas->chips[0]) + nand_release(nand_to_mtd(oxnas->chips[0])); + + clk_disable_unprepare(oxnas->clk); + return 0; } -subsys_initcall(oxnas_register_nand); +static const struct of_device_id oxnas_nand_match[] = { + { .compatible = "oxsemi,ox820-nand" }, + {}, +}; +MODULE_DEVICE_TABLE(of, oxnas_nand_match); + +static struct platform_driver oxnas_nand_driver = { + .probe = oxnas_nand_probe, + .remove = oxnas_nand_remove, + .driver = { + .name = "oxnas_nand", + .of_match_table = oxnas_nand_match, + }, +}; + +module_platform_driver(oxnas_nand_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Neil Armstrong "); +MODULE_DESCRIPTION("Oxnas NAND driver"); +MODULE_ALIAS("platform:oxnas_nand"); -- cgit v1.1