diff options
Diffstat (limited to 'drivers/mtd/nand/raw/lpc32xx_slc.c')
| -rw-r--r-- | drivers/mtd/nand/raw/lpc32xx_slc.c | 131 |
1 files changed, 59 insertions, 72 deletions
diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c index a2c5fdc875bd..3ca30e7dce33 100644 --- a/drivers/mtd/nand/raw/lpc32xx_slc.c +++ b/drivers/mtd/nand/raw/lpc32xx_slc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * NXP LPC32XX NAND SLC driver * @@ -7,16 +8,6 @@ * * Copyright © 2011 NXP Semiconductors * Copyright © 2012 Roland Stigge - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/slab.h> @@ -32,10 +23,8 @@ #include <linux/mm.h> #include <linux/dma-mapping.h> #include <linux/dmaengine.h> -#include <linux/mtd/nand_ecc.h> -#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/mtd/lpc32xx_slc.h> #define LPC32XX_MODNAME "lpc32xx-nand" @@ -218,7 +207,6 @@ struct lpc32xx_nand_cfg_slc { uint32_t rwidth; uint32_t rhold; uint32_t rsetup; - int wp_gpio; struct mtd_partition *parts; unsigned num_parts; }; @@ -227,6 +215,7 @@ struct lpc32xx_nand_host { struct nand_chip nand_chip; struct lpc32xx_slc_platform_data *pdata; struct clk *clk; + struct gpio_desc *wp_gpio; void __iomem *io_base; struct lpc32xx_nand_cfg_slc *ncfg; @@ -319,8 +308,8 @@ static int lpc32xx_nand_device_ready(struct nand_chip *chip) */ static void lpc32xx_wp_enable(struct lpc32xx_nand_host *host) { - if (gpio_is_valid(host->ncfg->wp_gpio)) - gpio_set_value(host->ncfg->wp_gpio, 0); + if (host->wp_gpio) + gpiod_set_value_cansleep(host->wp_gpio, 1); } /* @@ -328,8 +317,8 @@ static void lpc32xx_wp_enable(struct lpc32xx_nand_host *host) */ static void lpc32xx_wp_disable(struct lpc32xx_nand_host *host) { - if (gpio_is_valid(host->ncfg->wp_gpio)) - gpio_set_value(host->ncfg->wp_gpio, 1); + if (host->wp_gpio) + gpiod_set_value_cansleep(host->wp_gpio, 0); } /* @@ -732,18 +721,22 @@ static int lpc32xx_nand_dma_setup(struct lpc32xx_nand_host *host) struct mtd_info *mtd = nand_to_mtd(&host->nand_chip); dma_cap_mask_t mask; - if (!host->pdata || !host->pdata->dma_filter) { - dev_err(mtd->dev.parent, "no DMA platform data\n"); - return -ENOENT; - } + host->dma_chan = dma_request_chan(mtd->dev.parent, "rx-tx"); + if (IS_ERR(host->dma_chan)) { + /* fallback to request using platform data */ + if (!host->pdata || !host->pdata->dma_filter) { + dev_err(mtd->dev.parent, "no DMA platform data\n"); + return -ENOENT; + } + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + host->dma_chan = dma_request_channel(mask, host->pdata->dma_filter, "nand-slc"); - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - host->dma_chan = dma_request_channel(mask, host->pdata->dma_filter, - "nand-slc"); - if (!host->dma_chan) { - dev_err(mtd->dev.parent, "Failed to request DMA channel\n"); - return -EBUSY; + if (!host->dma_chan) { + dev_err(mtd->dev.parent, "Failed to request DMA channel\n"); + return -EBUSY; + } } return 0; @@ -774,8 +767,6 @@ static struct lpc32xx_nand_cfg_slc *lpc32xx_parse_dt(struct device *dev) return NULL; } - ncfg->wp_gpio = of_get_named_gpio(np, "gpios", 0); - return ncfg; } @@ -784,6 +775,9 @@ static int lpc32xx_nand_attach_chip(struct nand_chip *chip) struct mtd_info *mtd = nand_to_mtd(chip); struct lpc32xx_nand_host *host = nand_get_controller_data(chip); + if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) + return 0; + /* OOB and ECC CPU and DMA work areas */ host->ecc_buf = (uint32_t *)(host->data_buf + LPC32XX_DMA_DATA_SIZE); @@ -795,11 +789,22 @@ static int lpc32xx_nand_attach_chip(struct nand_chip *chip) if (mtd->writesize <= 512) mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops); + chip->ecc.placement = NAND_ECC_PLACEMENT_INTERLEAVED; /* These sizes remain the same regardless of page size */ chip->ecc.size = 256; + chip->ecc.strength = 1; chip->ecc.bytes = LPC32XX_SLC_DEV_ECC_BYTES; chip->ecc.prepad = 0; chip->ecc.postpad = 0; + chip->ecc.read_page_raw = lpc32xx_nand_read_page_raw_syndrome; + chip->ecc.read_page = lpc32xx_nand_read_page_syndrome; + chip->ecc.write_page_raw = lpc32xx_nand_write_page_raw_syndrome; + chip->ecc.write_page = lpc32xx_nand_write_page_syndrome; + chip->ecc.write_oob = lpc32xx_nand_write_oob_syndrome; + chip->ecc.read_oob = lpc32xx_nand_read_oob_syndrome; + chip->ecc.calculate = lpc32xx_nand_ecc_calculate; + chip->ecc.correct = rawnand_sw_hamming_correct; + chip->ecc.hwctl = lpc32xx_nand_ecc_enable; /* * Use a custom BBT marker setup for small page FLASH that @@ -835,8 +840,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) if (!host) return -ENOMEM; - rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); - host->io_base = devm_ioremap_resource(&pdev->dev, rc); + host->io_base = devm_platform_get_and_ioremap_resource(pdev, 0, &rc); if (IS_ERR(host->io_base)) return PTR_ERR(host->io_base); @@ -848,14 +852,18 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) "Missing or bad NAND config from device tree\n"); return -ENOENT; } - if (host->ncfg->wp_gpio == -EPROBE_DEFER) - return -EPROBE_DEFER; - if (gpio_is_valid(host->ncfg->wp_gpio) && devm_gpio_request(&pdev->dev, - host->ncfg->wp_gpio, "NAND WP")) { - dev_err(&pdev->dev, "GPIO not available\n"); - return -EBUSY; + + /* Start with WP disabled, if available */ + host->wp_gpio = devm_gpiod_get_optional(&pdev->dev, NULL, GPIOD_OUT_LOW); + res = PTR_ERR_OR_ZERO(host->wp_gpio); + if (res) { + if (res != -EPROBE_DEFER) + dev_err(&pdev->dev, "WP GPIO is not available: %d\n", + res); + return res; } - lpc32xx_wp_disable(host); + + gpiod_set_consumer_name(host->wp_gpio, "NAND WP"); host->pdata = dev_get_platdata(&pdev->dev); @@ -867,15 +875,12 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) mtd->dev.parent = &pdev->dev; /* Get NAND clock */ - host->clk = devm_clk_get(&pdev->dev, NULL); + host->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(host->clk)) { dev_err(&pdev->dev, "Clock failure\n"); res = -ENOENT; goto enable_wp; } - res = clk_prepare_enable(host->clk); - if (res) - goto enable_wp; /* Set NAND IO addresses and command/ready functions */ chip->legacy.IO_ADDR_R = SLC_DATA(host->io_base); @@ -890,20 +895,9 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); /* NAND callbacks for LPC32xx SLC hardware */ - chip->ecc.mode = NAND_ECC_HW_SYNDROME; chip->legacy.read_byte = lpc32xx_nand_read_byte; chip->legacy.read_buf = lpc32xx_nand_read_buf; chip->legacy.write_buf = lpc32xx_nand_write_buf; - chip->ecc.read_page_raw = lpc32xx_nand_read_page_raw_syndrome; - chip->ecc.read_page = lpc32xx_nand_read_page_syndrome; - chip->ecc.write_page_raw = lpc32xx_nand_write_page_raw_syndrome; - chip->ecc.write_page = lpc32xx_nand_write_page_syndrome; - chip->ecc.write_oob = lpc32xx_nand_write_oob_syndrome; - chip->ecc.read_oob = lpc32xx_nand_read_oob_syndrome; - chip->ecc.calculate = lpc32xx_nand_ecc_calculate; - chip->ecc.correct = nand_correct_data; - chip->ecc.strength = 1; - chip->ecc.hwctl = lpc32xx_nand_ecc_enable; /* * Allocate a large enough buffer for a single huge page plus @@ -914,13 +908,13 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) GFP_KERNEL); if (host->data_buf == NULL) { res = -ENOMEM; - goto unprepare_clk; + goto enable_wp; } res = lpc32xx_nand_dma_setup(host); if (res) { res = -EIO; - goto unprepare_clk; + goto enable_wp; } /* Find NAND device */ @@ -941,8 +935,6 @@ cleanup_nand: nand_cleanup(chip); release_dma: dma_release_channel(host->dma_chan); -unprepare_clk: - clk_disable_unprepare(host->clk); enable_wp: lpc32xx_wp_enable(host); @@ -952,12 +944,16 @@ enable_wp: /* * Remove NAND device. */ -static int lpc32xx_nand_remove(struct platform_device *pdev) +static void lpc32xx_nand_remove(struct platform_device *pdev) { uint32_t tmp; struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); + struct nand_chip *chip = &host->nand_chip; + int ret; - nand_release(&host->nand_chip); + ret = mtd_device_unregister(nand_to_mtd(chip)); + WARN_ON(ret); + nand_cleanup(chip); dma_release_channel(host->dma_chan); /* Force CE high */ @@ -965,13 +961,9 @@ static int lpc32xx_nand_remove(struct platform_device *pdev) tmp &= ~SLCCFG_CE_LOW; writel(tmp, SLC_CTRL(host->io_base)); - clk_disable_unprepare(host->clk); lpc32xx_wp_enable(host); - - return 0; } -#ifdef CONFIG_PM static int lpc32xx_nand_resume(struct platform_device *pdev) { struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); @@ -1010,11 +1002,6 @@ static int lpc32xx_nand_suspend(struct platform_device *pdev, pm_message_t pm) return 0; } -#else -#define lpc32xx_nand_resume NULL -#define lpc32xx_nand_suspend NULL -#endif - static const struct of_device_id lpc32xx_nand_match[] = { { .compatible = "nxp,lpc3220-slc" }, { /* sentinel */ }, @@ -1024,8 +1011,8 @@ MODULE_DEVICE_TABLE(of, lpc32xx_nand_match); static struct platform_driver lpc32xx_nand_driver = { .probe = lpc32xx_nand_probe, .remove = lpc32xx_nand_remove, - .resume = lpc32xx_nand_resume, - .suspend = lpc32xx_nand_suspend, + .resume = pm_ptr(lpc32xx_nand_resume), + .suspend = pm_ptr(lpc32xx_nand_suspend), .driver = { .name = LPC32XX_MODNAME, .of_match_table = lpc32xx_nand_match, |
