From f170c6fb7036dca779bbcb2e0fea4b4eed3201b1 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Mon, 27 Nov 2017 23:52:56 +0100 Subject: mtd: onenand: Remove obsolete url from Kconfig help Samsung website no longer host information about OneNAND, delete it. Signed-off-by: Ladislav Michl Signed-off-by: Boris Brezillon --- drivers/mtd/onenand/Kconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/mtd/onenand') diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig index dcae2f6a2b11..aaeb30458139 100644 --- a/drivers/mtd/onenand/Kconfig +++ b/drivers/mtd/onenand/Kconfig @@ -4,8 +4,7 @@ menuconfig MTD_ONENAND depends on HAS_IOMEM help This enables support for accessing all type of OneNAND flash - devices. For further information see - + devices. if MTD_ONENAND -- cgit v1.2.3 From 8b5c51a45c39c6d4f05c6f87d05f54b6c28f7c36 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Thu, 14 Dec 2017 07:03:49 +0100 Subject: mtd: onenand: samsung: use devm_ function to simplify code and fix some leaks Convert all error handling code in 's3c_onenand_probe()' to resource-managed alternatives in order to simplify code. This fixes a resource leak if 'platform_get_resource()' fails at line 872. The 'request_irq()' at line 971 was also un-balanced. It is now resource-managed. Signed-off-by: Christophe JAILLET Signed-off-by: Boris Brezillon --- drivers/mtd/onenand/samsung.c | 165 ++++++++---------------------------------- 1 file changed, 30 insertions(+), 135 deletions(-) (limited to 'drivers/mtd/onenand') diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c index af0ac1a7bf8f..bad59f5cc56d 100644 --- a/drivers/mtd/onenand/samsung.c +++ b/drivers/mtd/onenand/samsung.c @@ -129,16 +129,13 @@ struct s3c_onenand { struct platform_device *pdev; enum soc_type type; void __iomem *base; - struct resource *base_res; void __iomem *ahb_addr; - struct resource *ahb_res; int bootram_command; void __iomem *page_buf; void __iomem *oob_buf; unsigned int (*mem_addr)(int fba, int fpa, int fsa); unsigned int (*cmd_map)(unsigned int type, unsigned int val); void __iomem *dma_addr; - struct resource *dma_res; unsigned long phys_base; struct completion complete; }; @@ -851,15 +848,14 @@ static int s3c_onenand_probe(struct platform_device *pdev) /* No need to check pdata. the platform data is optional */ size = sizeof(struct mtd_info) + sizeof(struct onenand_chip); - mtd = kzalloc(size, GFP_KERNEL); + mtd = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); if (!mtd) return -ENOMEM; - onenand = kzalloc(sizeof(struct s3c_onenand), GFP_KERNEL); - if (!onenand) { - err = -ENOMEM; - goto onenand_fail; - } + onenand = devm_kzalloc(&pdev->dev, sizeof(struct s3c_onenand), + GFP_KERNEL); + if (!onenand) + return -ENOMEM; this = (struct onenand_chip *) &mtd[1]; mtd->priv = this; @@ -870,26 +866,12 @@ static int s3c_onenand_probe(struct platform_device *pdev) s3c_onenand_setup(mtd); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) { - dev_err(&pdev->dev, "no memory resource defined\n"); - return -ENOENT; - goto ahb_resource_failed; - } + onenand->base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(onenand->base)) + return PTR_ERR(onenand->base); - onenand->base_res = request_mem_region(r->start, resource_size(r), - pdev->name); - if (!onenand->base_res) { - dev_err(&pdev->dev, "failed to request memory resource\n"); - err = -EBUSY; - goto resource_failed; - } + onenand->phys_base = r->start; - onenand->base = ioremap(r->start, resource_size(r)); - if (!onenand->base) { - dev_err(&pdev->dev, "failed to map memory resource\n"); - err = -EFAULT; - goto ioremap_failed; - } /* Set onenand_chip also */ this->base = onenand->base; @@ -898,40 +880,20 @@ static int s3c_onenand_probe(struct platform_device *pdev) if (onenand->type != TYPE_S5PC110) { r = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!r) { - dev_err(&pdev->dev, "no buffer memory resource defined\n"); - err = -ENOENT; - goto ahb_resource_failed; - } - - onenand->ahb_res = request_mem_region(r->start, resource_size(r), - pdev->name); - if (!onenand->ahb_res) { - dev_err(&pdev->dev, "failed to request buffer memory resource\n"); - err = -EBUSY; - goto ahb_resource_failed; - } - - onenand->ahb_addr = ioremap(r->start, resource_size(r)); - if (!onenand->ahb_addr) { - dev_err(&pdev->dev, "failed to map buffer memory resource\n"); - err = -EINVAL; - goto ahb_ioremap_failed; - } + onenand->ahb_addr = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(onenand->ahb_addr)) + return PTR_ERR(onenand->ahb_addr); /* Allocate 4KiB BufferRAM */ - onenand->page_buf = kzalloc(SZ_4K, GFP_KERNEL); - if (!onenand->page_buf) { - err = -ENOMEM; - goto page_buf_fail; - } + onenand->page_buf = devm_kzalloc(&pdev->dev, SZ_4K, + GFP_KERNEL); + if (!onenand->page_buf) + return -ENOMEM; /* Allocate 128 SpareRAM */ - onenand->oob_buf = kzalloc(128, GFP_KERNEL); - if (!onenand->oob_buf) { - err = -ENOMEM; - goto oob_buf_fail; - } + onenand->oob_buf = devm_kzalloc(&pdev->dev, 128, GFP_KERNEL); + if (!onenand->oob_buf) + return -ENOMEM; /* S3C doesn't handle subpage write */ mtd->subpage_sft = 0; @@ -939,28 +901,9 @@ static int s3c_onenand_probe(struct platform_device *pdev) } else { /* S5PC110 */ r = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!r) { - dev_err(&pdev->dev, "no dma memory resource defined\n"); - err = -ENOENT; - goto dma_resource_failed; - } - - onenand->dma_res = request_mem_region(r->start, resource_size(r), - pdev->name); - if (!onenand->dma_res) { - dev_err(&pdev->dev, "failed to request dma memory resource\n"); - err = -EBUSY; - goto dma_resource_failed; - } - - onenand->dma_addr = ioremap(r->start, resource_size(r)); - if (!onenand->dma_addr) { - dev_err(&pdev->dev, "failed to map dma memory resource\n"); - err = -EINVAL; - goto dma_ioremap_failed; - } - - onenand->phys_base = onenand->base_res->start; + onenand->dma_addr = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(onenand->dma_addr)) + return PTR_ERR(onenand->dma_addr); s5pc110_dma_ops = s5pc110_dma_poll; /* Interrupt support */ @@ -968,19 +911,19 @@ static int s3c_onenand_probe(struct platform_device *pdev) if (r) { init_completion(&onenand->complete); s5pc110_dma_ops = s5pc110_dma_irq; - err = request_irq(r->start, s5pc110_onenand_irq, - IRQF_SHARED, "onenand", &onenand); + err = devm_request_irq(&pdev->dev, r->start, + s5pc110_onenand_irq, + IRQF_SHARED, "onenand", + &onenand); if (err) { dev_err(&pdev->dev, "failed to get irq\n"); - goto scan_failed; + return err; } } } - if (onenand_scan(mtd, 1)) { - err = -EFAULT; - goto scan_failed; - } + if (onenand_scan(mtd, 1)) + return -EFAULT; if (onenand->type != TYPE_S5PC110) { /* S3C doesn't handle subpage write */ @@ -998,36 +941,6 @@ static int s3c_onenand_probe(struct platform_device *pdev) platform_set_drvdata(pdev, mtd); return 0; - -scan_failed: - if (onenand->dma_addr) - iounmap(onenand->dma_addr); -dma_ioremap_failed: - if (onenand->dma_res) - release_mem_region(onenand->dma_res->start, - resource_size(onenand->dma_res)); - kfree(onenand->oob_buf); -oob_buf_fail: - kfree(onenand->page_buf); -page_buf_fail: - if (onenand->ahb_addr) - iounmap(onenand->ahb_addr); -ahb_ioremap_failed: - if (onenand->ahb_res) - release_mem_region(onenand->ahb_res->start, - resource_size(onenand->ahb_res)); -dma_resource_failed: -ahb_resource_failed: - iounmap(onenand->base); -ioremap_failed: - if (onenand->base_res) - release_mem_region(onenand->base_res->start, - resource_size(onenand->base_res)); -resource_failed: - kfree(onenand); -onenand_fail: - kfree(mtd); - return err; } static int s3c_onenand_remove(struct platform_device *pdev) @@ -1035,25 +948,7 @@ static int s3c_onenand_remove(struct platform_device *pdev) struct mtd_info *mtd = platform_get_drvdata(pdev); onenand_release(mtd); - if (onenand->ahb_addr) - iounmap(onenand->ahb_addr); - if (onenand->ahb_res) - release_mem_region(onenand->ahb_res->start, - resource_size(onenand->ahb_res)); - if (onenand->dma_addr) - iounmap(onenand->dma_addr); - if (onenand->dma_res) - release_mem_region(onenand->dma_res->start, - resource_size(onenand->dma_res)); - - iounmap(onenand->base); - release_mem_region(onenand->base_res->start, - resource_size(onenand->base_res)); - - kfree(onenand->oob_buf); - kfree(onenand->page_buf); - kfree(onenand); - kfree(mtd); + return 0; } -- cgit v1.2.3 From ad99be47726ccb0bcbc4b0fac12c20cba78e4c0d Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Thu, 14 Dec 2017 07:03:50 +0100 Subject: mtd: onenand: samsung: return an error if 'mtd_device_parse_register()' fails If 'mtd_device_parse_register()' fails, we still return 0 which mean success. Return the error code instead, as done in all the other error handling paths. Signed-off-by: Christophe JAILLET Signed-off-by: Boris Brezillon --- drivers/mtd/onenand/samsung.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/mtd/onenand') diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c index bad59f5cc56d..c5416d6ad571 100644 --- a/drivers/mtd/onenand/samsung.c +++ b/drivers/mtd/onenand/samsung.c @@ -937,6 +937,11 @@ static int s3c_onenand_probe(struct platform_device *pdev) err = mtd_device_parse_register(mtd, NULL, NULL, pdata ? pdata->parts : NULL, pdata ? pdata->nr_parts : 0); + if (err) { + dev_err(&pdev->dev, "failed to parse partitions and register the MTD device\n"); + onenand_release(mtd); + return err; + } platform_set_drvdata(pdev, mtd); -- cgit v1.2.3 From 0598344df0f093eba3c6e0af3caa934929c4b3aa Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Thu, 14 Dec 2017 07:03:51 +0100 Subject: mtd: onenand: samsung: Propagate the error returned by 'onenand_scan()' Propagate the error code returned by 'onenand_scan()' instead of a hard-coded -EFAULT. Signed-off-by: Christophe JAILLET Signed-off-by: Boris Brezillon --- drivers/mtd/onenand/samsung.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/mtd/onenand') diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c index c5416d6ad571..7e84a1bb91f3 100644 --- a/drivers/mtd/onenand/samsung.c +++ b/drivers/mtd/onenand/samsung.c @@ -922,8 +922,9 @@ static int s3c_onenand_probe(struct platform_device *pdev) } } - if (onenand_scan(mtd, 1)) - return -EFAULT; + err = onenand_scan(mtd, 1); + if (err) + return err; if (onenand->type != TYPE_S5PC110) { /* S3C doesn't handle subpage write */ -- cgit v1.2.3 From 24f648c2ba5d4492ce6c923c9cf3fdb3c6d821bd Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Thu, 14 Dec 2017 07:03:52 +0100 Subject: mtd: onenand: samsung: Remove a useless include This include is not needed, so remove it. Signed-off-by: Christophe JAILLET Signed-off-by: Boris Brezillon --- drivers/mtd/onenand/samsung.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/mtd/onenand') diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c index 7e84a1bb91f3..dfdfb478ba35 100644 --- a/drivers/mtd/onenand/samsung.c +++ b/drivers/mtd/onenand/samsung.c @@ -25,8 +25,6 @@ #include #include -#include - #include "samsung.h" enum soc_type { -- cgit v1.2.3 From e6854e029b16bfd059194aabaa738d6d0d982cac Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Fri, 12 Jan 2018 14:13:36 +0100 Subject: mtd: onenand: omap2: Remove regulator support As no platform data user sets regulator_can_sleep, regulator code is no-op and can be deleted. Signed-off-by: Ladislav Michl Reviewed-by: Peter Ujfalusi Reviewed-by: Sebastian Reichel Acked-by: Roger Quadros Tested-by: Tony Lindgren Tested-by: Aaro Koskinen Signed-off-by: Boris Brezillon --- drivers/mtd/onenand/omap2.c | 42 +----------------------------------------- 1 file changed, 1 insertion(+), 41 deletions(-) (limited to 'drivers/mtd/onenand') diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index 24a1388d3031..a03e1fe4aa48 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #include @@ -59,7 +58,6 @@ struct omap2_onenand { int dma_channel; int freq; int (*setup)(void __iomem *base, int *freq_ptr); - struct regulator *regulator; u8 flags; }; @@ -583,30 +581,6 @@ static void omap2_onenand_shutdown(struct platform_device *pdev) memset((__force void *)c->onenand.base, 0, ONENAND_BUFRAM_SIZE); } -static int omap2_onenand_enable(struct mtd_info *mtd) -{ - int ret; - struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd); - - ret = regulator_enable(c->regulator); - if (ret != 0) - dev_err(&c->pdev->dev, "can't enable regulator\n"); - - return ret; -} - -static int omap2_onenand_disable(struct mtd_info *mtd) -{ - int ret; - struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd); - - ret = regulator_disable(c->regulator); - if (ret != 0) - dev_err(&c->pdev->dev, "can't disable regulator\n"); - - return ret; -} - static int omap2_onenand_probe(struct platform_device *pdev) { struct omap_onenand_platform_data *pdata; @@ -726,22 +700,11 @@ static int omap2_onenand_probe(struct platform_device *pdev) } } - if (pdata->regulator_can_sleep) { - c->regulator = regulator_get(&pdev->dev, "vonenand"); - if (IS_ERR(c->regulator)) { - dev_err(&pdev->dev, "Failed to get regulator\n"); - r = PTR_ERR(c->regulator); - goto err_release_dma; - } - c->onenand.enable = omap2_onenand_enable; - c->onenand.disable = omap2_onenand_disable; - } - if (pdata->skip_initial_unlocking) this->options |= ONENAND_SKIP_INITIAL_UNLOCKING; if ((r = onenand_scan(&c->mtd, 1)) < 0) - goto err_release_regulator; + goto err_release_dma; r = mtd_device_register(&c->mtd, pdata ? pdata->parts : NULL, pdata ? pdata->nr_parts : 0); @@ -754,8 +717,6 @@ static int omap2_onenand_probe(struct platform_device *pdev) err_release_onenand: onenand_release(&c->mtd); -err_release_regulator: - regulator_put(c->regulator); err_release_dma: if (c->dma_channel != -1) omap_free_dma(c->dma_channel); @@ -779,7 +740,6 @@ static int omap2_onenand_remove(struct platform_device *pdev) struct omap2_onenand *c = dev_get_drvdata(&pdev->dev); onenand_release(&c->mtd); - regulator_put(c->regulator); if (c->dma_channel != -1) omap_free_dma(c->dma_channel); omap2_onenand_shutdown(pdev); -- cgit v1.2.3 From 11066d42952ad254aa59043a9a71737e41705c5f Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Fri, 12 Jan 2018 14:13:56 +0100 Subject: mtd: onenand: omap2: Remove skip initial unlocking support No platform data user sets skip_initial_unlocking, so remove test for this field. Signed-off-by: Ladislav Michl Reviewed-by: Peter Ujfalusi Reviewed-by: Sebastian Reichel Acked-by: Roger Quadros Tested-by: Tony Lindgren Tested-by: Aaro Koskinen Signed-off-by: Boris Brezillon --- drivers/mtd/onenand/omap2.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/mtd/onenand') diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index a03e1fe4aa48..93bd94337b35 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -700,9 +700,6 @@ static int omap2_onenand_probe(struct platform_device *pdev) } } - if (pdata->skip_initial_unlocking) - this->options |= ONENAND_SKIP_INITIAL_UNLOCKING; - if ((r = onenand_scan(&c->mtd, 1)) < 0) goto err_release_dma; -- cgit v1.2.3 From fafc0b3a9f7b586ae1261b2f78e389bd67df92d7 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Fri, 12 Jan 2018 14:14:17 +0100 Subject: mtd: onenand: omap2: Remove partitioning support from platform data No platform data user setups partitioning information, so remove. Signed-off-by: Ladislav Michl Reviewed-by: Peter Ujfalusi Reviewed-by: Sebastian Reichel Acked-by: Roger Quadros Tested-by: Tony Lindgren Tested-by: Aaro Koskinen Signed-off-by: Boris Brezillon --- drivers/mtd/onenand/omap2.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/mtd/onenand') diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index 93bd94337b35..883993bbe40b 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -703,8 +703,7 @@ static int omap2_onenand_probe(struct platform_device *pdev) if ((r = onenand_scan(&c->mtd, 1)) < 0) goto err_release_dma; - r = mtd_device_register(&c->mtd, pdata ? pdata->parts : NULL, - pdata ? pdata->nr_parts : 0); + r = mtd_device_register(&c->mtd, NULL, 0); if (r) goto err_release_onenand; -- cgit v1.2.3 From d120568883a4676852caab2e3545c0d1a623376b Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Fri, 12 Jan 2018 14:14:54 +0100 Subject: mtd: onenand: omap2: Account waiting time as waiting on IO Use wait_for_completion_io_timeout, which has an impact on how the task is accounted in scheduling stats. Signed-off-by: Ladislav Michl Reviewed-by: Peter Ujfalusi Reviewed-by: Sebastian Reichel Acked-by: Roger Quadros Tested-by: Tony Lindgren Tested-by: Aaro Koskinen Signed-off-by: Boris Brezillon --- drivers/mtd/onenand/omap2.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/mtd/onenand') diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index 883993bbe40b..0e7772e16d75 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -170,9 +170,8 @@ static int omap2_onenand_wait(struct mtd_info *mtd, int state) if (result == 0) { int retry_cnt = 0; retry: - result = wait_for_completion_timeout(&c->irq_done, - msecs_to_jiffies(20)); - if (result == 0) { + if (!wait_for_completion_io_timeout(&c->irq_done, + msecs_to_jiffies(20))) { /* Timeout after 20ms */ ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS); if (ctrl & ONENAND_CTRL_ONGO && -- cgit v1.2.3 From 3621311695f5b1a9396ae95098ac904328eefde7 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 12 Jan 2018 14:15:25 +0100 Subject: mtd: onenand: omap2: Simplify the DMA setup for various paths We have 4 functions containing almost identical DMA setup code. Create one function which can set up the DMA for both read and write and use this in place for the setup code in the driver. The new function will use wait_for_completion_io_timeout() and it will figure out the best data_type to be used for the transfer instead of hardwiring 32 or 16 bit data. Signed-off-by: Peter Ujfalusi Signed-off-by: Ladislav Michl Reviewed-by: Sebastian Reichel Acked-by: Roger Quadros Tested-by: Tony Lindgren Tested-by: Aaro Koskinen Signed-off-by: Boris Brezillon --- drivers/mtd/onenand/omap2.c | 109 ++++++++++++++++++-------------------------- 1 file changed, 45 insertions(+), 64 deletions(-) (limited to 'drivers/mtd/onenand') diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index 0e7772e16d75..d22163271dc9 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -288,6 +288,33 @@ static inline int omap2_onenand_bufferram_offset(struct mtd_info *mtd, int area) return 0; } +static inline int omap2_onenand_dma_transfer(struct omap2_onenand *c, + dma_addr_t src, dma_addr_t dst, + size_t count) +{ + int data_type = __ffs((src | dst | count)); + + if (data_type > OMAP_DMA_DATA_TYPE_S32) + data_type = OMAP_DMA_DATA_TYPE_S32; + + omap_set_dma_transfer_params(c->dma_channel, data_type, + count / BIT(data_type), 1, 0, 0, 0); + omap_set_dma_src_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, + src, 0, 0); + omap_set_dma_dest_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, + dst, 0, 0); + + reinit_completion(&c->dma_done); + omap_start_dma(c->dma_channel); + if (!wait_for_completion_io_timeout(&c->dma_done, + msecs_to_jiffies(20))) { + omap_stop_dma(c->dma_channel); + return -ETIMEDOUT; + } + + return 0; +} + #if defined(CONFIG_ARCH_OMAP3) || defined(MULTI_OMAP2) static int omap3_onenand_read_bufferram(struct mtd_info *mtd, int area, @@ -298,10 +325,9 @@ static int omap3_onenand_read_bufferram(struct mtd_info *mtd, int area, struct onenand_chip *this = mtd->priv; dma_addr_t dma_src, dma_dst; int bram_offset; - unsigned long timeout; void *buf = (void *)buffer; size_t xtra; - volatile unsigned *done; + int ret; bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset; if (bram_offset & 3 || (size_t)buf & 3 || count < 384) @@ -338,25 +364,10 @@ static int omap3_onenand_read_bufferram(struct mtd_info *mtd, int area, goto out_copy; } - omap_set_dma_transfer_params(c->dma_channel, OMAP_DMA_DATA_TYPE_S32, - count >> 2, 1, 0, 0, 0); - omap_set_dma_src_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, - dma_src, 0, 0); - omap_set_dma_dest_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, - dma_dst, 0, 0); - - reinit_completion(&c->dma_done); - omap_start_dma(c->dma_channel); - - timeout = jiffies + msecs_to_jiffies(20); - done = &c->dma_done.done; - while (time_before(jiffies, timeout)) - if (*done) - break; - + ret = omap2_onenand_dma_transfer(c, dma_src, dma_dst, count); dma_unmap_single(&c->pdev->dev, dma_dst, count, DMA_FROM_DEVICE); - if (!*done) { + if (ret) { dev_err(&c->pdev->dev, "timeout waiting for DMA\n"); goto out_copy; } @@ -376,9 +387,8 @@ static int omap3_onenand_write_bufferram(struct mtd_info *mtd, int area, struct onenand_chip *this = mtd->priv; dma_addr_t dma_src, dma_dst; int bram_offset; - unsigned long timeout; void *buf = (void *)buffer; - volatile unsigned *done; + int ret; bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset; if (bram_offset & 3 || (size_t)buf & 3 || count < 384) @@ -409,25 +419,10 @@ static int omap3_onenand_write_bufferram(struct mtd_info *mtd, int area, return -1; } - omap_set_dma_transfer_params(c->dma_channel, OMAP_DMA_DATA_TYPE_S32, - count >> 2, 1, 0, 0, 0); - omap_set_dma_src_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, - dma_src, 0, 0); - omap_set_dma_dest_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, - dma_dst, 0, 0); - - reinit_completion(&c->dma_done); - omap_start_dma(c->dma_channel); - - timeout = jiffies + msecs_to_jiffies(20); - done = &c->dma_done.done; - while (time_before(jiffies, timeout)) - if (*done) - break; - + ret = omap2_onenand_dma_transfer(c, dma_src, dma_dst, count); dma_unmap_single(&c->pdev->dev, dma_src, count, DMA_TO_DEVICE); - if (!*done) { + if (ret) { dev_err(&c->pdev->dev, "timeout waiting for DMA\n"); goto out_copy; } @@ -466,7 +461,7 @@ static int omap2_onenand_read_bufferram(struct mtd_info *mtd, int area, struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd); struct onenand_chip *this = mtd->priv; dma_addr_t dma_src, dma_dst; - int bram_offset; + int bram_offset, ret; bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset; /* DMA is not used. Revisit PM requirements before enabling it. */ @@ -488,20 +483,13 @@ static int omap2_onenand_read_bufferram(struct mtd_info *mtd, int area, return -1; } - omap_set_dma_transfer_params(c->dma_channel, OMAP_DMA_DATA_TYPE_S32, - count / 4, 1, 0, 0, 0); - omap_set_dma_src_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, - dma_src, 0, 0); - omap_set_dma_dest_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, - dma_dst, 0, 0); - - reinit_completion(&c->dma_done); - omap_start_dma(c->dma_channel); - wait_for_completion(&c->dma_done); - + ret = omap2_onenand_dma_transfer(c, dma_src, dma_dst, count); dma_unmap_single(&c->pdev->dev, dma_dst, count, DMA_FROM_DEVICE); - return 0; + if (ret) + dev_err(&c->pdev->dev, "timeout waiting for DMA\n"); + + return ret; } static int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area, @@ -511,7 +499,7 @@ static int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area, struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd); struct onenand_chip *this = mtd->priv; dma_addr_t dma_src, dma_dst; - int bram_offset; + int bram_offset, ret; bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset; /* DMA is not used. Revisit PM requirements before enabling it. */ @@ -533,20 +521,13 @@ static int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area, return -1; } - omap_set_dma_transfer_params(c->dma_channel, OMAP_DMA_DATA_TYPE_S16, - count / 2, 1, 0, 0, 0); - omap_set_dma_src_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, - dma_src, 0, 0); - omap_set_dma_dest_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, - dma_dst, 0, 0); - - reinit_completion(&c->dma_done); - omap_start_dma(c->dma_channel); - wait_for_completion(&c->dma_done); - + ret = omap2_onenand_dma_transfer(c, dma_src, dma_dst, count); dma_unmap_single(&c->pdev->dev, dma_src, count, DMA_TO_DEVICE); - return 0; + if (ret) + dev_err(&c->pdev->dev, "timeout waiting for DMA\n"); + + return ret; } #else -- cgit v1.2.3 From fb25070afdf07cc62282c27357dc30ef3d7ef262 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Fri, 12 Jan 2018 14:15:45 +0100 Subject: mtd: onenand: omap2: Unify OMAP2 and OMAP3 DMA implementation Since the very first commit (36cd4fb5d277: "[MTD] [OneNAND] Add OMAP2 / OMAP3 OneNAND driver") DMA is disabled for OMAP2. Later fixes thus went only into OMAP3 specific DMA functions which turned out not to be so OMAP3 specific, so merge those two implementations. Signed-off-by: Ladislav Michl Reviewed-by: Peter Ujfalusi Reviewed-by: Sebastian Reichel Acked-by: Roger Quadros Tested-by: Tony Lindgren Tested-by: Aaro Koskinen Signed-off-by: Boris Brezillon --- drivers/mtd/onenand/omap2.c | 129 ++------------------------------------------ 1 file changed, 4 insertions(+), 125 deletions(-) (limited to 'drivers/mtd/onenand') diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index d22163271dc9..36314124488d 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -315,9 +315,7 @@ static inline int omap2_onenand_dma_transfer(struct omap2_onenand *c, return 0; } -#if defined(CONFIG_ARCH_OMAP3) || defined(MULTI_OMAP2) - -static int omap3_onenand_read_bufferram(struct mtd_info *mtd, int area, +static int omap2_onenand_read_bufferram(struct mtd_info *mtd, int area, unsigned char *buffer, int offset, size_t count) { @@ -379,7 +377,7 @@ out_copy: return 0; } -static int omap3_onenand_write_bufferram(struct mtd_info *mtd, int area, +static int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area, const unsigned char *buffer, int offset, size_t count) { @@ -434,120 +432,6 @@ out_copy: return 0; } -#else - -static int omap3_onenand_read_bufferram(struct mtd_info *mtd, int area, - unsigned char *buffer, int offset, - size_t count) -{ - return -ENOSYS; -} - -static int omap3_onenand_write_bufferram(struct mtd_info *mtd, int area, - const unsigned char *buffer, - int offset, size_t count) -{ - return -ENOSYS; -} - -#endif - -#if defined(CONFIG_ARCH_OMAP2) || defined(MULTI_OMAP2) - -static int omap2_onenand_read_bufferram(struct mtd_info *mtd, int area, - unsigned char *buffer, int offset, - size_t count) -{ - struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd); - struct onenand_chip *this = mtd->priv; - dma_addr_t dma_src, dma_dst; - int bram_offset, ret; - - bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset; - /* DMA is not used. Revisit PM requirements before enabling it. */ - if (1 || (c->dma_channel < 0) || - ((void *) buffer >= (void *) high_memory) || (bram_offset & 3) || - (((unsigned int) buffer) & 3) || (count < 1024) || (count & 3)) { - memcpy(buffer, (__force void *)(this->base + bram_offset), - count); - return 0; - } - - dma_src = c->phys_base + bram_offset; - dma_dst = dma_map_single(&c->pdev->dev, buffer, count, - DMA_FROM_DEVICE); - if (dma_mapping_error(&c->pdev->dev, dma_dst)) { - dev_err(&c->pdev->dev, - "Couldn't DMA map a %d byte buffer\n", - count); - return -1; - } - - ret = omap2_onenand_dma_transfer(c, dma_src, dma_dst, count); - dma_unmap_single(&c->pdev->dev, dma_dst, count, DMA_FROM_DEVICE); - - if (ret) - dev_err(&c->pdev->dev, "timeout waiting for DMA\n"); - - return ret; -} - -static int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area, - const unsigned char *buffer, - int offset, size_t count) -{ - struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd); - struct onenand_chip *this = mtd->priv; - dma_addr_t dma_src, dma_dst; - int bram_offset, ret; - - bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset; - /* DMA is not used. Revisit PM requirements before enabling it. */ - if (1 || (c->dma_channel < 0) || - ((void *) buffer >= (void *) high_memory) || (bram_offset & 3) || - (((unsigned int) buffer) & 3) || (count < 1024) || (count & 3)) { - memcpy((__force void *)(this->base + bram_offset), buffer, - count); - return 0; - } - - dma_src = dma_map_single(&c->pdev->dev, (void *) buffer, count, - DMA_TO_DEVICE); - dma_dst = c->phys_base + bram_offset; - if (dma_mapping_error(&c->pdev->dev, dma_src)) { - dev_err(&c->pdev->dev, - "Couldn't DMA map a %d byte buffer\n", - count); - return -1; - } - - ret = omap2_onenand_dma_transfer(c, dma_src, dma_dst, count); - dma_unmap_single(&c->pdev->dev, dma_src, count, DMA_TO_DEVICE); - - if (ret) - dev_err(&c->pdev->dev, "timeout waiting for DMA\n"); - - return ret; -} - -#else - -static int omap2_onenand_read_bufferram(struct mtd_info *mtd, int area, - unsigned char *buffer, int offset, - size_t count) -{ - return -ENOSYS; -} - -static int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area, - const unsigned char *buffer, - int offset, size_t count) -{ - return -ENOSYS; -} - -#endif - static struct platform_driver omap2_onenand_driver; static void omap2_onenand_shutdown(struct platform_device *pdev) @@ -671,13 +555,8 @@ static int omap2_onenand_probe(struct platform_device *pdev) this = &c->onenand; if (c->dma_channel >= 0) { this->wait = omap2_onenand_wait; - if (c->flags & ONENAND_IN_OMAP34XX) { - this->read_bufferram = omap3_onenand_read_bufferram; - this->write_bufferram = omap3_onenand_write_bufferram; - } else { - this->read_bufferram = omap2_onenand_read_bufferram; - this->write_bufferram = omap2_onenand_write_bufferram; - } + this->read_bufferram = omap2_onenand_read_bufferram; + this->write_bufferram = omap2_onenand_write_bufferram; } if ((r = onenand_scan(&c->mtd, 1)) < 0) -- cgit v1.2.3 From 3ed6a4d1de2c5855a9e0164872b6adfd6b7a4215 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 12 Jan 2018 14:16:08 +0100 Subject: mtd: onenand: omap2: Convert to use dmaengine for memcpy Do not use the legacy and deprecated omap-dma interface for setting up the memcpy. Signed-off-by: Peter Ujfalusi Signed-off-by: Ladislav Michl Reviewed-by: Sebastian Reichel Acked-by: Roger Quadros Tested-by: Tony Lindgren Tested-by: Aaro Koskinen Signed-off-by: Boris Brezillon --- drivers/mtd/onenand/omap2.c | 80 +++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 42 deletions(-) (limited to 'drivers/mtd/onenand') diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index 36314124488d..c9ff67100ef4 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -39,8 +40,6 @@ #include #include -#include - #define DRIVER_NAME "omap2-onenand" #define ONENAND_BUFRAM_SIZE (1024 * 5) @@ -55,17 +54,15 @@ struct omap2_onenand { struct onenand_chip onenand; struct completion irq_done; struct completion dma_done; - int dma_channel; + struct dma_chan *dma_chan; int freq; int (*setup)(void __iomem *base, int *freq_ptr); u8 flags; }; -static void omap2_onenand_dma_cb(int lch, u16 ch_status, void *data) +static void omap2_onenand_dma_complete_func(void *completion) { - struct omap2_onenand *c = data; - - complete(&c->dma_done); + complete(completion); } static irqreturn_t omap2_onenand_interrupt(int irq, void *dev_id) @@ -292,23 +289,31 @@ static inline int omap2_onenand_dma_transfer(struct omap2_onenand *c, dma_addr_t src, dma_addr_t dst, size_t count) { - int data_type = __ffs((src | dst | count)); + struct dma_async_tx_descriptor *tx; + dma_cookie_t cookie; - if (data_type > OMAP_DMA_DATA_TYPE_S32) - data_type = OMAP_DMA_DATA_TYPE_S32; - - omap_set_dma_transfer_params(c->dma_channel, data_type, - count / BIT(data_type), 1, 0, 0, 0); - omap_set_dma_src_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, - src, 0, 0); - omap_set_dma_dest_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, - dst, 0, 0); + tx = dmaengine_prep_dma_memcpy(c->dma_chan, dst, src, count, 0); + if (!tx) { + dev_err(&c->pdev->dev, "Failed to prepare DMA memcpy\n"); + return -EIO; + } reinit_completion(&c->dma_done); - omap_start_dma(c->dma_channel); + + tx->callback = omap2_onenand_dma_complete_func; + tx->callback_param = &c->dma_done; + + cookie = tx->tx_submit(tx); + if (dma_submit_error(cookie)) { + dev_err(&c->pdev->dev, "Failed to do DMA tx_submit\n"); + return -EIO; + } + + dma_async_issue_pending(c->dma_chan); + if (!wait_for_completion_io_timeout(&c->dma_done, msecs_to_jiffies(20))) { - omap_stop_dma(c->dma_channel); + dmaengine_terminate_sync(c->dma_chan); return -ETIMEDOUT; } @@ -468,8 +473,7 @@ static int omap2_onenand_probe(struct platform_device *pdev) c->flags = pdata->flags; c->gpmc_cs = pdata->cs; c->gpio_irq = pdata->gpio_irq; - c->dma_channel = pdata->dma_channel; - if (c->dma_channel < 0) { + if (pdata->dma_channel < 0) { /* if -1, don't use DMA */ c->gpio_irq = 0; } @@ -521,25 +525,17 @@ static int omap2_onenand_probe(struct platform_device *pdev) goto err_release_gpio; } - if (c->dma_channel >= 0) { - r = omap_request_dma(0, pdev->dev.driver->name, - omap2_onenand_dma_cb, (void *) c, - &c->dma_channel); - if (r == 0) { - omap_set_dma_write_mode(c->dma_channel, - OMAP_DMA_WRITE_NON_POSTED); - omap_set_dma_src_data_pack(c->dma_channel, 1); - omap_set_dma_src_burst_mode(c->dma_channel, - OMAP_DMA_DATA_BURST_8); - omap_set_dma_dest_data_pack(c->dma_channel, 1); - omap_set_dma_dest_burst_mode(c->dma_channel, - OMAP_DMA_DATA_BURST_8); - } else { + if (pdata->dma_channel >= 0) { + dma_cap_mask_t mask; + + dma_cap_zero(mask); + dma_cap_set(DMA_MEMCPY, mask); + + c->dma_chan = dma_request_channel(mask, NULL, NULL); + if (!c->dma_chan) dev_info(&pdev->dev, "failed to allocate DMA for OneNAND, " "using PIO instead\n"); - c->dma_channel = -1; - } } dev_info(&pdev->dev, "initializing on CS%d, phys base 0x%08lx, virtual " @@ -553,7 +549,7 @@ static int omap2_onenand_probe(struct platform_device *pdev) mtd_set_of_node(&c->mtd, pdata->of_node); this = &c->onenand; - if (c->dma_channel >= 0) { + if (c->dma_chan) { this->wait = omap2_onenand_wait; this->read_bufferram = omap2_onenand_read_bufferram; this->write_bufferram = omap2_onenand_write_bufferram; @@ -573,8 +569,8 @@ static int omap2_onenand_probe(struct platform_device *pdev) err_release_onenand: onenand_release(&c->mtd); err_release_dma: - if (c->dma_channel != -1) - omap_free_dma(c->dma_channel); + if (c->dma_chan) + dma_release_channel(c->dma_chan); if (c->gpio_irq) free_irq(gpio_to_irq(c->gpio_irq), c); err_release_gpio: @@ -595,8 +591,8 @@ static int omap2_onenand_remove(struct platform_device *pdev) struct omap2_onenand *c = dev_get_drvdata(&pdev->dev); onenand_release(&c->mtd); - if (c->dma_channel != -1) - omap_free_dma(c->dma_channel); + if (c->dma_chan) + dma_release_channel(c->dma_chan); omap2_onenand_shutdown(pdev); if (c->gpio_irq) { free_irq(gpio_to_irq(c->gpio_irq), c); -- cgit v1.2.3 From f5229331f13b84389cd71a58ccd5e15e5cb091c2 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Fri, 12 Jan 2018 14:16:28 +0100 Subject: mtd: onenand: omap2: Do not make delay for GPIO OMAP3 specific Second commit in driver history (782b7a367d81: "[MTD] [OneNAND] OMAP3: add delay for GPIO") added quirk for waiting until GPIO line settle. As DMA was disabled for OMAP2 boards, chances are this problem was not OMAP3 specific and as it is just one register read, previous test for SoC type is approximately as expensive as read itself. Make delay unconditional, which allows removing SoC specific code alltogether. Signed-off-by: Ladislav Michl Reviewed-by: Peter Ujfalusi Reviewed-by: Sebastian Reichel Acked-by: Roger Quadros Tested-by: Tony Lindgren Tested-by: Aaro Koskinen Signed-off-by: Boris Brezillon --- drivers/mtd/onenand/omap2.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/mtd/onenand') diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index c9ff67100ef4..e4857a41760d 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -57,7 +57,6 @@ struct omap2_onenand { struct dma_chan *dma_chan; int freq; int (*setup)(void __iomem *base, int *freq_ptr); - u8 flags; }; static void omap2_onenand_dma_complete_func(void *completion) @@ -148,9 +147,8 @@ static int omap2_onenand_wait(struct mtd_info *mtd, int state) if (!(syscfg & ONENAND_SYS_CFG1_IOBE)) { syscfg |= ONENAND_SYS_CFG1_IOBE; write_reg(c, syscfg, ONENAND_REG_SYS_CFG1); - if (c->flags & ONENAND_IN_OMAP34XX) - /* Add a delay to let GPIO settle */ - syscfg = read_reg(c, ONENAND_REG_SYS_CFG1); + /* Add a delay to let GPIO settle */ + syscfg = read_reg(c, ONENAND_REG_SYS_CFG1); } reinit_completion(&c->irq_done); @@ -470,7 +468,6 @@ static int omap2_onenand_probe(struct platform_device *pdev) init_completion(&c->irq_done); init_completion(&c->dma_done); - c->flags = pdata->flags; c->gpmc_cs = pdata->cs; c->gpio_irq = pdata->gpio_irq; if (pdata->dma_channel < 0) { -- cgit v1.2.3 From bdaca9345d41fd9420995469d27603ea62054691 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Fri, 12 Jan 2018 14:16:57 +0100 Subject: mtd: onenand: omap2: Decouple DMA enabling from INT pin availability INT pin (gpio_irq) is not really needed for DMA but only for notification when a command that needs wait has completed. DMA memcpy can be still used even without gpio_irq available, so enable it unconditionally. Signed-off-by: Ladislav Michl Reviewed-by: Peter Ujfalusi Tested-by: Tony Lindgren Tested-by: Aaro Koskinen Acked-by: Roger Quadros Signed-off-by: Boris Brezillon --- drivers/mtd/onenand/omap2.c | 52 ++++++++++++++++++--------------------------- 1 file changed, 21 insertions(+), 31 deletions(-) (limited to 'drivers/mtd/onenand') diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index e4857a41760d..1cd78a076759 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -152,17 +152,13 @@ static int omap2_onenand_wait(struct mtd_info *mtd, int state) } reinit_completion(&c->irq_done); - if (c->gpio_irq) { - result = gpio_get_value(c->gpio_irq); - if (result == -1) { - ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS); - intr = read_reg(c, ONENAND_REG_INTERRUPT); - wait_err("gpio error", state, ctrl, intr); - return -EIO; - } - } else - result = 0; - if (result == 0) { + result = gpio_get_value(c->gpio_irq); + if (result < 0) { + ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS); + intr = read_reg(c, ONENAND_REG_INTERRUPT); + wait_err("gpio error", state, ctrl, intr); + return -EIO; + } else if (result == 0) { int retry_cnt = 0; retry: if (!wait_for_completion_io_timeout(&c->irq_done, @@ -450,6 +446,7 @@ static void omap2_onenand_shutdown(struct platform_device *pdev) static int omap2_onenand_probe(struct platform_device *pdev) { + dma_cap_mask_t mask; struct omap_onenand_platform_data *pdata; struct omap2_onenand *c; struct onenand_chip *this; @@ -513,31 +510,25 @@ static int omap2_onenand_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to request GPIO%d for " "OneNAND\n", c->gpio_irq); goto err_iounmap; - } - gpio_direction_input(c->gpio_irq); + } + gpio_direction_input(c->gpio_irq); - if ((r = request_irq(gpio_to_irq(c->gpio_irq), - omap2_onenand_interrupt, IRQF_TRIGGER_RISING, - pdev->dev.driver->name, c)) < 0) - goto err_release_gpio; - } + if ((r = request_irq(gpio_to_irq(c->gpio_irq), + omap2_onenand_interrupt, IRQF_TRIGGER_RISING, + pdev->dev.driver->name, c)) < 0) + goto err_release_gpio; - if (pdata->dma_channel >= 0) { - dma_cap_mask_t mask; + this->wait = omap2_onenand_wait; + } - dma_cap_zero(mask); - dma_cap_set(DMA_MEMCPY, mask); + dma_cap_zero(mask); + dma_cap_set(DMA_MEMCPY, mask); - c->dma_chan = dma_request_channel(mask, NULL, NULL); - if (!c->dma_chan) - dev_info(&pdev->dev, - "failed to allocate DMA for OneNAND, " - "using PIO instead\n"); - } + c->dma_chan = dma_request_channel(mask, NULL, NULL); dev_info(&pdev->dev, "initializing on CS%d, phys base 0x%08lx, virtual " - "base %p, freq %d MHz\n", c->gpmc_cs, c->phys_base, - c->onenand.base, c->freq); + "base %p, freq %d MHz, %s mode\n", c->gpmc_cs, c->phys_base, + c->onenand.base, c->freq, c->dma_chan ? "DMA" : "PIO"); c->pdev = pdev; c->mtd.priv = &c->onenand; @@ -547,7 +538,6 @@ static int omap2_onenand_probe(struct platform_device *pdev) this = &c->onenand; if (c->dma_chan) { - this->wait = omap2_onenand_wait; this->read_bufferram = omap2_onenand_read_bufferram; this->write_bufferram = omap2_onenand_write_bufferram; } -- cgit v1.2.3 From a758f50f10cfcf863f95372ff52e0d8d22fda9ba Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Fri, 12 Jan 2018 14:17:25 +0100 Subject: mtd: onenand: omap2: Configure driver from DT Move away from platform data configuration and use pure DT approach. Use generic probe function to deal with OneNAND node and remove now useless gpmc_probe_onenand_child function. Import sync mode timing calculation function from mach-omap2/gpmc-onenand.c Signed-off-by: Ladislav Michl Reviewed-by: Peter Ujfalusi Tested-by: Tony Lindgren Tested-by: Aaro Koskinen Acked-by: Roger Quadros Signed-off-by: Boris Brezillon --- drivers/mtd/onenand/Kconfig | 4 +- drivers/mtd/onenand/omap2.c | 250 +++++++++++++++++++++++++++----------------- 2 files changed, 156 insertions(+), 98 deletions(-) (limited to 'drivers/mtd/onenand') diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig index aaeb30458139..9dc15748947b 100644 --- a/drivers/mtd/onenand/Kconfig +++ b/drivers/mtd/onenand/Kconfig @@ -25,9 +25,11 @@ config MTD_ONENAND_GENERIC config MTD_ONENAND_OMAP2 tristate "OneNAND on OMAP2/OMAP3 support" depends on ARCH_OMAP2 || ARCH_OMAP3 + depends on OF || COMPILE_TEST help - Support for a OneNAND flash device connected to an OMAP2/OMAP3 CPU + Support for a OneNAND flash device connected to an OMAP2/OMAP3 SoC via the GPMC memory controller. + Enable dmaengine and gpiolib for better performance. config MTD_ONENAND_SAMSUNG tristate "OneNAND on Samsung SOC controller support" diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index 1cd78a076759..2ce73fb6da1c 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include #include @@ -35,10 +37,9 @@ #include #include #include -#include +#include #include -#include #define DRIVER_NAME "omap2-onenand" @@ -48,15 +49,12 @@ struct omap2_onenand { struct platform_device *pdev; int gpmc_cs; unsigned long phys_base; - unsigned int mem_size; - int gpio_irq; + struct gpio_desc *int_gpiod; struct mtd_info mtd; struct onenand_chip onenand; struct completion irq_done; struct completion dma_done; struct dma_chan *dma_chan; - int freq; - int (*setup)(void __iomem *base, int *freq_ptr); }; static void omap2_onenand_dma_complete_func(void *completion) @@ -84,6 +82,65 @@ static inline void write_reg(struct omap2_onenand *c, unsigned short value, writew(value, c->onenand.base + reg); } +static int omap2_onenand_set_cfg(struct omap2_onenand *c, + bool sr, bool sw, + int latency, int burst_len) +{ + unsigned short reg = ONENAND_SYS_CFG1_RDY | ONENAND_SYS_CFG1_INT; + + reg |= latency << ONENAND_SYS_CFG1_BRL_SHIFT; + + switch (burst_len) { + case 0: /* continuous */ + break; + case 4: + reg |= ONENAND_SYS_CFG1_BL_4; + break; + case 8: + reg |= ONENAND_SYS_CFG1_BL_8; + break; + case 16: + reg |= ONENAND_SYS_CFG1_BL_16; + break; + case 32: + reg |= ONENAND_SYS_CFG1_BL_32; + break; + default: + return -EINVAL; + } + + if (latency > 5) + reg |= ONENAND_SYS_CFG1_HF; + if (latency > 7) + reg |= ONENAND_SYS_CFG1_VHF; + if (sr) + reg |= ONENAND_SYS_CFG1_SYNC_READ; + if (sw) + reg |= ONENAND_SYS_CFG1_SYNC_WRITE; + + write_reg(c, reg, ONENAND_REG_SYS_CFG1); + + return 0; +} + +static int omap2_onenand_get_freq(int ver) +{ + switch ((ver >> 4) & 0xf) { + case 0: + return 40; + case 1: + return 54; + case 2: + return 66; + case 3: + return 83; + case 4: + return 104; + } + + return -EINVAL; +} + static void wait_err(char *msg, int state, unsigned int ctrl, unsigned int intr) { printk(KERN_ERR "onenand_wait: %s! state %d ctrl 0x%04x intr 0x%04x\n", @@ -152,12 +209,12 @@ static int omap2_onenand_wait(struct mtd_info *mtd, int state) } reinit_completion(&c->irq_done); - result = gpio_get_value(c->gpio_irq); + result = gpiod_get_value(c->int_gpiod); if (result < 0) { ctrl = read_reg(c, ONENAND_REG_CTRL_STATUS); intr = read_reg(c, ONENAND_REG_INTERRUPT); wait_err("gpio error", state, ctrl, intr); - return -EIO; + return result; } else if (result == 0) { int retry_cnt = 0; retry: @@ -431,8 +488,6 @@ out_copy: return 0; } -static struct platform_driver omap2_onenand_driver; - static void omap2_onenand_shutdown(struct platform_device *pdev) { struct omap2_onenand *c = dev_get_drvdata(&pdev->dev); @@ -446,105 +501,117 @@ static void omap2_onenand_shutdown(struct platform_device *pdev) static int omap2_onenand_probe(struct platform_device *pdev) { + u32 val; dma_cap_mask_t mask; - struct omap_onenand_platform_data *pdata; - struct omap2_onenand *c; - struct onenand_chip *this; - int r; + int freq, latency, r; struct resource *res; + struct omap2_onenand *c; + struct gpmc_onenand_info info; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "error getting memory resource\n"); + return -EINVAL; + } - pdata = dev_get_platdata(&pdev->dev); - if (pdata == NULL) { - dev_err(&pdev->dev, "platform data missing\n"); - return -ENODEV; + r = of_property_read_u32(np, "reg", &val); + if (r) { + dev_err(dev, "reg not found in DT\n"); + return r; } - c = kzalloc(sizeof(struct omap2_onenand), GFP_KERNEL); + c = devm_kzalloc(dev, sizeof(struct omap2_onenand), GFP_KERNEL); if (!c) return -ENOMEM; init_completion(&c->irq_done); init_completion(&c->dma_done); - c->gpmc_cs = pdata->cs; - c->gpio_irq = pdata->gpio_irq; - if (pdata->dma_channel < 0) { - /* if -1, don't use DMA */ - c->gpio_irq = 0; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - r = -EINVAL; - dev_err(&pdev->dev, "error getting memory resource\n"); - goto err_kfree; - } - + c->gpmc_cs = val; c->phys_base = res->start; - c->mem_size = resource_size(res); - - if (request_mem_region(c->phys_base, c->mem_size, - pdev->dev.driver->name) == NULL) { - dev_err(&pdev->dev, "Cannot reserve memory region at 0x%08lx, size: 0x%x\n", - c->phys_base, c->mem_size); - r = -EBUSY; - goto err_kfree; - } - c->onenand.base = ioremap(c->phys_base, c->mem_size); - if (c->onenand.base == NULL) { - r = -ENOMEM; - goto err_release_mem_region; - } - if (pdata->onenand_setup != NULL) { - r = pdata->onenand_setup(c->onenand.base, &c->freq); - if (r < 0) { - dev_err(&pdev->dev, "Onenand platform setup failed: " - "%d\n", r); - goto err_iounmap; - } - c->setup = pdata->onenand_setup; + c->onenand.base = devm_ioremap_resource(dev, res); + if (IS_ERR(c->onenand.base)) { + dev_err(dev, "Cannot reserve memory region at 0x%08x, size: 0x%x\n", + res->start, resource_size(res)); + return PTR_ERR(c->onenand.base); } - if (c->gpio_irq) { - if ((r = gpio_request(c->gpio_irq, "OneNAND irq")) < 0) { - dev_err(&pdev->dev, "Failed to request GPIO%d for " - "OneNAND\n", c->gpio_irq); - goto err_iounmap; - } - gpio_direction_input(c->gpio_irq); + c->int_gpiod = devm_gpiod_get_optional(dev, "int", GPIOD_IN); + if (IS_ERR(c->int_gpiod)) { + r = PTR_ERR(c->int_gpiod); + /* Just try again if this happens */ + if (r != -EPROBE_DEFER) + dev_err(dev, "error getting gpio: %d\n", r); + return r; + } - if ((r = request_irq(gpio_to_irq(c->gpio_irq), - omap2_onenand_interrupt, IRQF_TRIGGER_RISING, - pdev->dev.driver->name, c)) < 0) - goto err_release_gpio; + if (c->int_gpiod) { + r = devm_request_irq(dev, gpiod_to_irq(c->int_gpiod), + omap2_onenand_interrupt, + IRQF_TRIGGER_RISING, "onenand", c); + if (r) + return r; - this->wait = omap2_onenand_wait; + c->onenand.wait = omap2_onenand_wait; } dma_cap_zero(mask); dma_cap_set(DMA_MEMCPY, mask); c->dma_chan = dma_request_channel(mask, NULL, NULL); - - dev_info(&pdev->dev, "initializing on CS%d, phys base 0x%08lx, virtual " - "base %p, freq %d MHz, %s mode\n", c->gpmc_cs, c->phys_base, - c->onenand.base, c->freq, c->dma_chan ? "DMA" : "PIO"); + if (c->dma_chan) { + c->onenand.read_bufferram = omap2_onenand_read_bufferram; + c->onenand.write_bufferram = omap2_onenand_write_bufferram; + } c->pdev = pdev; c->mtd.priv = &c->onenand; + c->mtd.dev.parent = dev; + mtd_set_of_node(&c->mtd, dev->of_node); - c->mtd.dev.parent = &pdev->dev; - mtd_set_of_node(&c->mtd, pdata->of_node); - - this = &c->onenand; - if (c->dma_chan) { - this->read_bufferram = omap2_onenand_read_bufferram; - this->write_bufferram = omap2_onenand_write_bufferram; - } + dev_info(dev, "initializing on CS%d (0x%08lx), va %p, %s mode\n", + c->gpmc_cs, c->phys_base, c->onenand.base, + c->dma_chan ? "DMA" : "PIO"); if ((r = onenand_scan(&c->mtd, 1)) < 0) goto err_release_dma; + freq = omap2_onenand_get_freq(c->onenand.version_id); + if (freq > 0) { + switch (freq) { + case 104: + latency = 7; + break; + case 83: + latency = 6; + break; + case 66: + latency = 5; + break; + case 56: + latency = 4; + break; + default: /* 40 MHz or lower */ + latency = 3; + break; + } + + r = gpmc_omap_onenand_set_timings(dev, c->gpmc_cs, + freq, latency, &info); + if (r) + goto err_release_onenand; + + r = omap2_onenand_set_cfg(c, info.sync_read, info.sync_write, + latency, info.burst_len); + if (r) + goto err_release_onenand; + + if (info.sync_read || info.sync_write) + dev_info(dev, "optimized timings for %d MHz\n", freq); + } + r = mtd_device_register(&c->mtd, NULL, 0); if (r) goto err_release_onenand; @@ -558,17 +625,6 @@ err_release_onenand: err_release_dma: if (c->dma_chan) dma_release_channel(c->dma_chan); - if (c->gpio_irq) - free_irq(gpio_to_irq(c->gpio_irq), c); -err_release_gpio: - if (c->gpio_irq) - gpio_free(c->gpio_irq); -err_iounmap: - iounmap(c->onenand.base); -err_release_mem_region: - release_mem_region(c->phys_base, c->mem_size); -err_kfree: - kfree(c); return r; } @@ -581,23 +637,23 @@ static int omap2_onenand_remove(struct platform_device *pdev) if (c->dma_chan) dma_release_channel(c->dma_chan); omap2_onenand_shutdown(pdev); - if (c->gpio_irq) { - free_irq(gpio_to_irq(c->gpio_irq), c); - gpio_free(c->gpio_irq); - } - iounmap(c->onenand.base); - release_mem_region(c->phys_base, c->mem_size); - kfree(c); return 0; } +static const struct of_device_id omap2_onenand_id_table[] = { + { .compatible = "ti,omap2-onenand", }, + {}, +}; +MODULE_DEVICE_TABLE(of, omap2_onenand_id_table); + static struct platform_driver omap2_onenand_driver = { .probe = omap2_onenand_probe, .remove = omap2_onenand_remove, .shutdown = omap2_onenand_shutdown, .driver = { .name = DRIVER_NAME, + .of_match_table = omap2_onenand_id_table, }, }; -- cgit v1.2.3 From 6837befde3a6b42a36c894a4f86bafdc6db82534 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 13 Jan 2018 17:56:16 +0100 Subject: mtd: onenand: samsung: remove incorrect __iomem annotation 'page_buf' and 'oob_buf' are allocated with 'devm_kzalloc()' and should not have __iomem decoration. Remove these decorations and some useless casting. Signed-off-by: Christophe JAILLET Signed-off-by: Boris Brezillon --- drivers/mtd/onenand/samsung.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/mtd/onenand') diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c index dfdfb478ba35..2e9d076e445a 100644 --- a/drivers/mtd/onenand/samsung.c +++ b/drivers/mtd/onenand/samsung.c @@ -129,8 +129,8 @@ struct s3c_onenand { void __iomem *base; void __iomem *ahb_addr; int bootram_command; - void __iomem *page_buf; - void __iomem *oob_buf; + void *page_buf; + void *oob_buf; unsigned int (*mem_addr)(int fba, int fpa, int fsa); unsigned int (*cmd_map)(unsigned int type, unsigned int val); void __iomem *dma_addr; @@ -408,8 +408,8 @@ static int s3c_onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, /* * Emulate Two BufferRAMs and access with 4 bytes pointer */ - m = (unsigned int *) onenand->page_buf; - s = (unsigned int *) onenand->oob_buf; + m = onenand->page_buf; + s = onenand->oob_buf; if (index) { m += (this->writesize >> 2); @@ -481,11 +481,11 @@ static unsigned char *s3c_get_bufferram(struct mtd_info *mtd, int area) unsigned char *p; if (area == ONENAND_DATARAM) { - p = (unsigned char *) onenand->page_buf; + p = onenand->page_buf; if (index == 1) p += this->writesize; } else { - p = (unsigned char *) onenand->oob_buf; + p = onenand->oob_buf; if (index == 1) p += mtd->oobsize; } -- cgit v1.2.3 From 82cea533aed44c1b8553fe782e1bc5e1262bd71a Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 16 Jan 2018 08:43:40 +0100 Subject: mtd: onenand: omap2: print resource using %pR format string The omap2 onenand driver is now available for compile-testing, which uncovers a warning in configurations that have a 64-bit resource_size_t: drivers/mtd/onenand/omap2.c: In function 'omap2_onenand_probe': drivers/mtd/onenand/omap2.c:536:54: error: format '%x' expects argument of type 'unsigned int', but argument 3 has type 'resource_size_t {aka long long unsigned int}' [-Werror=format=] dev_err(dev, "Cannot reserve memory region at 0x%08x, size: 0x%x\n", drivers/mtd/onenand/omap2.c:536:66: error: format '%x' expects argument of type 'unsigned int', but argument 4 has type 'resource_size_t {aka long long unsigned int}' [-Werror=format=] Changing the format string to the special %pR simplifies the code and lets it do the right thing in that configuration, while avoiding the warning. Fixes: a758f50f10cf ("mtd: onenand: omap2: Configure driver from DT") Signed-off-by: Arnd Bergmann Reviewed-by: Sebastian Reichel Reviewed-by: Peter Ujfalusi Signed-off-by: Boris Brezillon --- drivers/mtd/onenand/omap2.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/mtd/onenand') diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index 2ce73fb6da1c..a4a2159bcfb7 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -533,8 +533,7 @@ static int omap2_onenand_probe(struct platform_device *pdev) c->onenand.base = devm_ioremap_resource(dev, res); if (IS_ERR(c->onenand.base)) { - dev_err(dev, "Cannot reserve memory region at 0x%08x, size: 0x%x\n", - res->start, resource_size(res)); + dev_err(dev, "Cannot reserve memory region %pR\n", res); return PTR_ERR(c->onenand.base); } -- cgit v1.2.3 From e6b90db83f8b33735fe5a008ff171f69527a5305 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 17 Jan 2018 11:25:33 +0000 Subject: mtd: onenand: omap2: Remove redundant dev_err call in omap2_onenand_probe() There is a error message within devm_ioremap_resource already, so remove the dev_err call to avoid redundant error message. Signed-off-by: Wei Yongjun Signed-off-by: Boris Brezillon --- drivers/mtd/onenand/omap2.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/mtd/onenand') diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index a4a2159bcfb7..87c34f607a75 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -532,10 +532,8 @@ static int omap2_onenand_probe(struct platform_device *pdev) c->phys_base = res->start; c->onenand.base = devm_ioremap_resource(dev, res); - if (IS_ERR(c->onenand.base)) { - dev_err(dev, "Cannot reserve memory region %pR\n", res); + if (IS_ERR(c->onenand.base)) return PTR_ERR(c->onenand.base); - } c->int_gpiod = devm_gpiod_get_optional(dev, "int", GPIOD_IN); if (IS_ERR(c->int_gpiod)) { -- cgit v1.2.3