[PATCH 1/2] nand: Add Marvell Orion NAND driver

Sebastian Hesselbarth sebastian.hesselbarth at gmail.com
Tue Aug 26 07:09:52 PDT 2014


On 08/23/2014 10:19 PM, Ezequiel Garcia wrote:
> This commit adds NAND support for the controller present in Kirkwood SoCs.

Ezequiel,

I just did a quick check through all public MVEBU datasheets. It looks
like Kirkwood is really the only SoC with this specific IP while Dove,
Armada 370, and XP have a different one.

> Signed-off-by: Ezequiel Garcia <ezequiel.garcia at free-electrons.com>
> ---
>   drivers/mtd/nand/Kconfig      |   7 ++
>   drivers/mtd/nand/Makefile     |   1 +
>   drivers/mtd/nand/nand_orion.c | 162 ++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 170 insertions(+)
>   create mode 100644 drivers/mtd/nand/nand_orion.c
>
> diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
> index 04fe3c8..ccf1f9c 100644
> --- a/drivers/mtd/nand/Kconfig
> +++ b/drivers/mtd/nand/Kconfig
> @@ -90,6 +90,13 @@ config NAND_OMAP_GPMC
>   	  Support for NAND flash using GPMC. GPMC is a common memory
>   	  interface found on Texas Instrument's OMAP platforms
>
> +config NAND_ORION
> +	bool
> +	prompt "Orion NAND driver"
> +	depends on ARCH_MVEBU

Therefore, we should limit this to ARCH_KIRKWOOD. Also, we could choose
to call the driver nand_kirkwood.c. OTOH, I am fine with Orion as long
as we find another good name for the other IP. Remember that Dove is
still kind-of-Orion.

FWIW,

Acked-by: Sebastian Hesselbarth <sebastian.hesselbarth at gmail.com>

I'll give it a try on Guruplug later, too.

> +	help
> +	  Support for the Orion NAND controller, present in Kirkwood SoCs.
> +
>   config NAND_ATMEL
>   	bool
>   	prompt "Atmel (AT91SAM9xxx) NAND driver"
> diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
> index a1414e1..02dacde 100644
> --- a/drivers/mtd/nand/Makefile
> +++ b/drivers/mtd/nand/Makefile
> @@ -10,6 +10,7 @@ obj-$(CONFIG_MTD_NAND_NOMADIK)		+= nomadik_nand.o
>   obj-$(CONFIG_NAND_IMX)			+= nand_imx.o
>   obj-$(CONFIG_NAND_IMX_BBM)		+= nand_imx_bbm.o
>   obj-$(CONFIG_NAND_OMAP_GPMC)		+= nand_omap_gpmc.o nand_omap_bch_decoder.o
> +obj-$(CONFIG_NAND_ORION)		+= nand_orion.o
>   obj-$(CONFIG_NAND_ATMEL)		+= atmel_nand.o
>   obj-$(CONFIG_NAND_S3C24XX)		+= nand_s3c24xx.o
>   pbl-$(CONFIG_NAND_S3C24XX)		+= nand_s3c24xx.o
> diff --git a/drivers/mtd/nand/nand_orion.c b/drivers/mtd/nand/nand_orion.c
> new file mode 100644
> index 0000000..9bdd3b4
> --- /dev/null
> +++ b/drivers/mtd/nand/nand_orion.c
> @@ -0,0 +1,162 @@
> +/*
> + * (C) Copyright 2014, Ezequiel Garcia <ezequiel.garcia at free-electrons.com>
> + *
> + * Based on Orion NAND driver from Linux (drivers/mtd/nand/orion_nand.c):
> + * Author: Tzachi Perelstein <tzachi at marvell.com>
> + *
> + * This file is licensed under  the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <common.h>
> +#include <driver.h>
> +#include <malloc.h>
> +#include <init.h>
> +#include <io.h>
> +#include <of_mtd.h>
> +#include <errno.h>
> +#include <linux/mtd/mtd.h>
> +#include <linux/mtd/nand.h>
> +#include <linux/clk.h>
> +
> +struct orion_nand {
> +	struct mtd_info mtd;
> +	struct nand_chip chip;
> +
> +	u8 ale;         /* address line number connected to ALE */
> +	u8 cle;         /* address line number connected to CLE */
> +};
> +
> +static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
> +{
> +	struct nand_chip *chip = mtd->priv;
> +	struct orion_nand *priv = chip->priv;
> +	u32 offs;
> +
> +	if (cmd == NAND_CMD_NONE)
> +		return;
> +
> +	if (ctrl & NAND_CLE)
> +		offs = (1 << priv->cle);
> +	else if (ctrl & NAND_ALE)
> +		offs = (1 << priv->ale);
> +	else
> +		return;
> +
> +	if (chip->options & NAND_BUSWIDTH_16)
> +		offs <<= 1;
> +
> +	writeb(cmd, chip->IO_ADDR_W + offs);
> +}
> +
> +static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
> +{
> +	struct nand_chip *chip = mtd->priv;
> +	void __iomem *io_base = chip->IO_ADDR_R;
> +	uint64_t *buf64;
> +	int i = 0;
> +
> +	while (len && (unsigned long)buf & 7) {
> +		*buf++ = readb(io_base);
> +		len--;
> +	}
> +	buf64 = (uint64_t *)buf;
> +	while (i < len/8) {
> +		/*
> +		 * Since GCC has no proper constraint (PR 43518)
> +		 * force x variable to r2/r3 registers as ldrd instruction
> +		 * requires first register to be even.
> +		 */
> +		register uint64_t x asm ("r2");
> +
> +		asm volatile ("ldrd\t%0, [%1]" : "=&r" (x) : "r" (io_base));
> +		buf64[i++] = x;
> +	}
> +	i *= 8;
> +	while (i < len)
> +		buf[i++] = readb(io_base);
> +}
> +
> +static int orion_nand_probe(struct device_d *dev)
> +{
> +	struct device_node *dev_node = dev->device_node;
> +	struct orion_nand *priv;
> +	struct mtd_info *mtd;
> +	struct nand_chip *chip;
> +	struct clk *clk;
> +	void __iomem *io_base;
> +	int width, ret;
> +	u32 val = 0;
> +
> +	priv = xzalloc(sizeof(struct orion_nand));
> +	if (!priv) {
> +		ret = -ENOMEM;
> +		goto no_res;
> +	}
> +	mtd = &priv->mtd;
> +	chip = &priv->chip;
> +
> +	io_base = dev_request_mem_region(dev, 0);
> +
> +	if (!of_property_read_u32(dev_node, "cle", &val))
> +		priv->cle = (u8)val;
> +	else
> +		priv->cle = 0;
> +
> +	if (!of_property_read_u32(dev_node, "ale", &val))
> +		priv->ale = (u8)val;
> +	else
> +		priv->ale = 1;
> +
> +	if (!of_property_read_u32(dev_node, "bank-width", &val))
> +		width = (u8)val * 8;
> +	else
> +		width = 8;
> +
> +	if (!of_property_read_u32(dev_node, "chip-delay", &val))
> +		chip->chip_delay = (u8)val;
> +
> +	mtd->parent = dev;
> +	mtd->priv = chip;
> +	chip->priv = priv;
> +	chip->IO_ADDR_R = chip->IO_ADDR_W = io_base;
> +	chip->cmd_ctrl = orion_nand_cmd_ctrl;
> +	chip->read_buf = orion_nand_read_buf;
> +	chip->ecc.mode = NAND_ECC_SOFT;
> +
> +	WARN(width > 16, "%d bit bus width out of range", width);
> +	if (width == 16)
> +		chip->options |= NAND_BUSWIDTH_16;
> +
> +	/* Not all platforms can gate the clock, so this is optional */
> +	clk = clk_get(dev, 0);
> +	if (!IS_ERR(clk))
> +		clk_enable(clk);
> +
> +	if (nand_scan(mtd, 1)) {
> +		ret = -ENXIO;
> +		goto no_dev;
> +	}
> +
> +	add_mtd_nand_device(mtd, "orion_nand");
> +	return 0;
> +no_dev:
> +	if (!IS_ERR(clk))
> +		clk_disable(clk);
> +no_res:
> +	free(priv);
> +	return ret;
> +}
> +
> +static __maybe_unused struct of_device_id orion_nand_compatible[] = {
> +	{ .compatible = "marvell,orion-nand", },
> +	{},
> +};
> +
> +static struct driver_d orion_nand_driver = {
> +	.name  = "orion_nand",
> +	.probe = orion_nand_probe,
> +	.of_compatible = DRV_OF_COMPAT(orion_nand_compatible),
> +};
> +device_platform_driver(orion_nand_driver);
>




More information about the barebox mailing list