2006-05-21 18:11:55 +01:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2006 Jonathan McDowell <noodles@earth.li>
|
|
|
|
*
|
2018-02-05 23:02:02 +01:00
|
|
|
* Derived from drivers/mtd/nand/toto.c (removed in v2.6.28)
|
2018-02-05 23:02:03 +01:00
|
|
|
* Copyright (c) 2003 Texas Instruments
|
|
|
|
* Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de>
|
|
|
|
*
|
2010-12-14 21:09:40 +01:00
|
|
|
* Converted to platform driver by Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
|
2018-02-05 23:02:00 +01:00
|
|
|
* Partially stolen from plat_nand.c
|
2006-05-21 18:11:55 +01:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* Overview:
|
|
|
|
* This is a device driver for the NAND flash device found on the
|
|
|
|
* Amstrad E3 (Delta).
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/delay.h>
|
2018-09-20 00:17:29 +02:00
|
|
|
#include <linux/gpio/consumer.h>
|
2018-11-11 08:55:08 +01:00
|
|
|
#include <linux/io.h>
|
2006-05-21 18:11:55 +01:00
|
|
|
#include <linux/mtd/mtd.h>
|
2017-08-04 17:29:10 +02:00
|
|
|
#include <linux/mtd/rawnand.h>
|
2006-05-21 18:11:55 +01:00
|
|
|
#include <linux/mtd/partitions.h>
|
2012-08-30 15:37:24 -07:00
|
|
|
#include <linux/platform_data/gpio-omap.h>
|
2018-11-11 08:55:08 +01:00
|
|
|
#include <linux/sizes.h>
|
2006-05-21 18:11:55 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* MTD structure for E3 (Delta)
|
|
|
|
*/
|
2018-09-20 00:52:54 +02:00
|
|
|
|
|
|
|
struct ams_delta_nand {
|
|
|
|
struct nand_chip nand_chip;
|
|
|
|
struct gpio_desc *gpiod_rdy;
|
|
|
|
struct gpio_desc *gpiod_nce;
|
|
|
|
struct gpio_desc *gpiod_nre;
|
|
|
|
struct gpio_desc *gpiod_nwp;
|
|
|
|
struct gpio_desc *gpiod_nwe;
|
|
|
|
struct gpio_desc *gpiod_ale;
|
|
|
|
struct gpio_desc *gpiod_cle;
|
|
|
|
void __iomem *io_base;
|
2018-09-20 00:52:55 +02:00
|
|
|
bool data_in;
|
2018-09-20 00:52:54 +02:00
|
|
|
};
|
2006-05-21 18:11:55 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Define partitions for flash devices
|
|
|
|
*/
|
|
|
|
|
2017-08-28 13:54:57 +05:30
|
|
|
static const struct mtd_partition partition_info[] = {
|
2006-05-21 18:11:55 +01:00
|
|
|
{ .name = "Kernel",
|
|
|
|
.offset = 0,
|
|
|
|
.size = 3 * SZ_1M + SZ_512K },
|
|
|
|
{ .name = "u-boot",
|
|
|
|
.offset = 3 * SZ_1M + SZ_512K,
|
|
|
|
.size = SZ_256K },
|
|
|
|
{ .name = "u-boot params",
|
|
|
|
.offset = 3 * SZ_1M + SZ_512K + SZ_256K,
|
|
|
|
.size = SZ_256K },
|
|
|
|
{ .name = "Amstrad LDR",
|
|
|
|
.offset = 4 * SZ_1M,
|
|
|
|
.size = SZ_256K },
|
|
|
|
{ .name = "File system",
|
|
|
|
.offset = 4 * SZ_1M + 1 * SZ_256K,
|
|
|
|
.size = 27 * SZ_1M },
|
|
|
|
{ .name = "PBL reserved",
|
|
|
|
.offset = 32 * SZ_1M - 3 * SZ_256K,
|
|
|
|
.size = 3 * SZ_256K },
|
|
|
|
};
|
|
|
|
|
2018-09-20 00:52:55 +02:00
|
|
|
static void ams_delta_io_write(struct ams_delta_nand *priv, u_char byte)
|
2006-05-21 18:11:55 +01:00
|
|
|
{
|
2018-10-15 21:41:29 +02:00
|
|
|
writew(byte, priv->io_base + OMAP_MPUIO_OUTPUT);
|
2018-09-20 00:52:54 +02:00
|
|
|
gpiod_set_value(priv->gpiod_nwe, 0);
|
2006-05-21 18:11:55 +01:00
|
|
|
ndelay(40);
|
2018-09-20 00:52:54 +02:00
|
|
|
gpiod_set_value(priv->gpiod_nwe, 1);
|
2006-05-21 18:11:55 +01:00
|
|
|
}
|
|
|
|
|
2018-09-20 00:52:55 +02:00
|
|
|
static u_char ams_delta_io_read(struct ams_delta_nand *priv)
|
2006-05-21 18:11:55 +01:00
|
|
|
{
|
|
|
|
u_char res;
|
|
|
|
|
2018-09-20 00:52:54 +02:00
|
|
|
gpiod_set_value(priv->gpiod_nre, 0);
|
2006-05-21 18:11:55 +01:00
|
|
|
ndelay(40);
|
2018-10-15 21:41:29 +02:00
|
|
|
res = readw(priv->io_base + OMAP_MPUIO_INPUT_LATCH);
|
2018-09-20 00:52:54 +02:00
|
|
|
gpiod_set_value(priv->gpiod_nre, 1);
|
2006-05-21 18:11:55 +01:00
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2018-09-20 00:52:55 +02:00
|
|
|
static void ams_delta_dir_input(struct ams_delta_nand *priv, bool in)
|
|
|
|
{
|
|
|
|
writew(in ? ~0 : 0, priv->io_base + OMAP_MPUIO_IO_CNTL);
|
|
|
|
priv->data_in = in;
|
|
|
|
}
|
|
|
|
|
2018-10-15 21:41:30 +02:00
|
|
|
static void ams_delta_write_buf(struct ams_delta_nand *priv, const u_char *buf,
|
2006-05-21 18:11:55 +01:00
|
|
|
int len)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2018-09-20 00:52:55 +02:00
|
|
|
if (priv->data_in)
|
|
|
|
ams_delta_dir_input(priv, false);
|
|
|
|
|
|
|
|
for (i = 0; i < len; i++)
|
|
|
|
ams_delta_io_write(priv, buf[i]);
|
2006-05-21 18:11:55 +01:00
|
|
|
}
|
|
|
|
|
2018-10-15 21:41:30 +02:00
|
|
|
static void ams_delta_read_buf(struct ams_delta_nand *priv, u_char *buf,
|
|
|
|
int len)
|
2006-05-21 18:11:55 +01:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2018-09-20 00:52:55 +02:00
|
|
|
if (!priv->data_in)
|
|
|
|
ams_delta_dir_input(priv, true);
|
|
|
|
|
|
|
|
for (i = 0; i < len; i++)
|
|
|
|
buf[i] = ams_delta_io_read(priv);
|
|
|
|
}
|
|
|
|
|
2018-10-15 21:41:30 +02:00
|
|
|
static void ams_delta_select_chip(struct nand_chip *this, int n)
|
2006-05-23 23:25:53 +02:00
|
|
|
{
|
2018-09-20 00:52:54 +02:00
|
|
|
struct ams_delta_nand *priv = nand_get_controller_data(this);
|
2006-05-23 23:25:53 +02:00
|
|
|
|
2018-10-15 21:41:30 +02:00
|
|
|
if (n > 0)
|
|
|
|
return;
|
2018-09-20 00:52:55 +02:00
|
|
|
|
2018-10-15 21:41:30 +02:00
|
|
|
gpiod_set_value(priv->gpiod_nce, n < 0);
|
2006-05-23 23:25:53 +02:00
|
|
|
}
|
|
|
|
|
2018-10-15 21:41:30 +02:00
|
|
|
static int ams_delta_exec_op(struct nand_chip *this,
|
|
|
|
const struct nand_operation *op, bool check_only)
|
2006-05-21 18:11:55 +01:00
|
|
|
{
|
2018-09-20 00:52:54 +02:00
|
|
|
struct ams_delta_nand *priv = nand_get_controller_data(this);
|
2018-10-15 21:41:30 +02:00
|
|
|
const struct nand_op_instr *instr;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (check_only)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (instr = op->instrs; instr < op->instrs + op->ninstrs; instr++) {
|
|
|
|
|
|
|
|
switch (instr->type) {
|
|
|
|
case NAND_OP_CMD_INSTR:
|
|
|
|
gpiod_set_value(priv->gpiod_cle, 1);
|
|
|
|
ams_delta_write_buf(priv, &instr->ctx.cmd.opcode, 1);
|
|
|
|
gpiod_set_value(priv->gpiod_cle, 0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NAND_OP_ADDR_INSTR:
|
|
|
|
gpiod_set_value(priv->gpiod_ale, 1);
|
|
|
|
ams_delta_write_buf(priv, instr->ctx.addr.addrs,
|
|
|
|
instr->ctx.addr.naddrs);
|
|
|
|
gpiod_set_value(priv->gpiod_ale, 0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NAND_OP_DATA_IN_INSTR:
|
|
|
|
ams_delta_read_buf(priv, instr->ctx.data.buf.in,
|
|
|
|
instr->ctx.data.len);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NAND_OP_DATA_OUT_INSTR:
|
|
|
|
ams_delta_write_buf(priv, instr->ctx.data.buf.out,
|
|
|
|
instr->ctx.data.len);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NAND_OP_WAITRDY_INSTR:
|
|
|
|
ret = priv->gpiod_rdy ?
|
|
|
|
nand_gpio_waitrdy(this, priv->gpiod_rdy,
|
|
|
|
instr->ctx.waitrdy.timeout_ms) :
|
|
|
|
nand_soft_waitrdy(this,
|
|
|
|
instr->ctx.waitrdy.timeout_ms);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
}
|
2018-09-20 00:52:54 +02:00
|
|
|
|
2018-10-15 21:41:30 +02:00
|
|
|
return ret;
|
2006-05-21 18:11:55 +01:00
|
|
|
}
|
|
|
|
|
2011-12-20 00:08:55 +01:00
|
|
|
|
2006-05-21 18:11:55 +01:00
|
|
|
/*
|
|
|
|
* Main initialization routine
|
|
|
|
*/
|
2012-11-19 13:23:07 -05:00
|
|
|
static int ams_delta_init(struct platform_device *pdev)
|
2006-05-21 18:11:55 +01:00
|
|
|
{
|
2018-09-20 00:52:54 +02:00
|
|
|
struct ams_delta_nand *priv;
|
2006-05-21 18:11:55 +01:00
|
|
|
struct nand_chip *this;
|
2018-09-20 00:52:54 +02:00
|
|
|
struct mtd_info *mtd;
|
2010-12-15 15:43:44 +01:00
|
|
|
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
|
|
void __iomem *io_base;
|
2006-05-21 18:11:55 +01:00
|
|
|
int err = 0;
|
|
|
|
|
2010-12-15 15:43:44 +01:00
|
|
|
if (!res)
|
|
|
|
return -ENXIO;
|
|
|
|
|
2006-05-21 18:11:55 +01:00
|
|
|
/* Allocate memory for MTD device structure and private data */
|
2018-09-20 00:52:54 +02:00
|
|
|
priv = devm_kzalloc(&pdev->dev, sizeof(struct ams_delta_nand),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!priv) {
|
2018-02-22 22:01:22 +05:30
|
|
|
pr_warn("Unable to allocate E3 NAND MTD device structure.\n");
|
2018-09-20 00:52:54 +02:00
|
|
|
return -ENOMEM;
|
2006-05-21 18:11:55 +01:00
|
|
|
}
|
2018-09-20 00:52:54 +02:00
|
|
|
this = &priv->nand_chip;
|
2006-05-21 18:11:55 +01:00
|
|
|
|
2018-09-20 00:52:54 +02:00
|
|
|
mtd = nand_to_mtd(this);
|
|
|
|
mtd->dev.parent = &pdev->dev;
|
2006-05-21 18:11:55 +01:00
|
|
|
|
mtd: ams-delta: fix request_mem_region() failure
A call to request_mem_region() has been introduced in the omap-gpio
driver recently (commit 96751fcbe5438e95514b025e9cee7a6d38038f40,
"gpio/omap: Use devm_ API and add request_mem_region"). This change
prevented the Amstrad Delta NAND driver, which was doing the same in
order to take control over OMAP MPU I/O lines that the NAND device hangs
off, from loading successfully.
The I/O lines and corresponding registers used by the NAND driver are a
subset of those used for the GPIO function. Then, to avoid run time
collisions, all MPUIO GPIO lines should be marked as requested while
initializing the NAND driver, and vice versa, a single MPUIO GPIO line
already requested before the NAND driver initialization is attempted
should prevent the NAND device from being started successfully.
There is another driver, omap-keypad, which also manipulates MPUIO
registers, but has never been calling request_mem_region() on startup,
so it's not affected by the change in the gpio-omap and works correctly.
It uses the depreciated omap_read/write functions for accessing MPUIO
registers. Unlike the NAND driver, these I/O lines and registers are
separate from those used by the GPIO driver. However, both register sets
are non-contiguous and overlapping, so it would be impractical to
request the two sets separately, one from the gpio-omap, the other form
the omap-keypad driver.
In order to solve all these issues correctly, a solution first suggested
by Artem Bityutskiy, then closer specified by Tony Lindgren while they
commented the initial version of this fix, should be implemented. The
gpio-omap driver should export a few functions which would allow the
other two drivers to access MPUIO registers in a safe manner instead of
trying to manage them in parallel to the GPIO driver. However, such a
big change, affecting 3 drivers all together, is not suitable for the rc
cycle, and should be prepared for the merge window. Then, an
alternative solution is proposed as a regression fix.
For the ams-delta NAND driver to initialize correctly in coexistence
with the changed GPIO driver, drop the request_mem_region() call from
the former, especially as this call is going to be removed while the
long-term solution is implemented.
Tested on Amstrad Delta.
Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
Acked-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2012-05-07 22:51:37 +02:00
|
|
|
/*
|
|
|
|
* Don't try to request the memory region from here,
|
|
|
|
* it should have been already requested from the
|
|
|
|
* gpio-omap driver and requesting it again would fail.
|
|
|
|
*/
|
2010-12-15 15:43:44 +01:00
|
|
|
|
|
|
|
io_base = ioremap(res->start, resource_size(res));
|
|
|
|
if (io_base == NULL) {
|
|
|
|
dev_err(&pdev->dev, "ioremap failed\n");
|
|
|
|
err = -EIO;
|
mtd: ams-delta: fix request_mem_region() failure
A call to request_mem_region() has been introduced in the omap-gpio
driver recently (commit 96751fcbe5438e95514b025e9cee7a6d38038f40,
"gpio/omap: Use devm_ API and add request_mem_region"). This change
prevented the Amstrad Delta NAND driver, which was doing the same in
order to take control over OMAP MPU I/O lines that the NAND device hangs
off, from loading successfully.
The I/O lines and corresponding registers used by the NAND driver are a
subset of those used for the GPIO function. Then, to avoid run time
collisions, all MPUIO GPIO lines should be marked as requested while
initializing the NAND driver, and vice versa, a single MPUIO GPIO line
already requested before the NAND driver initialization is attempted
should prevent the NAND device from being started successfully.
There is another driver, omap-keypad, which also manipulates MPUIO
registers, but has never been calling request_mem_region() on startup,
so it's not affected by the change in the gpio-omap and works correctly.
It uses the depreciated omap_read/write functions for accessing MPUIO
registers. Unlike the NAND driver, these I/O lines and registers are
separate from those used by the GPIO driver. However, both register sets
are non-contiguous and overlapping, so it would be impractical to
request the two sets separately, one from the gpio-omap, the other form
the omap-keypad driver.
In order to solve all these issues correctly, a solution first suggested
by Artem Bityutskiy, then closer specified by Tony Lindgren while they
commented the initial version of this fix, should be implemented. The
gpio-omap driver should export a few functions which would allow the
other two drivers to access MPUIO registers in a safe manner instead of
trying to manage them in parallel to the GPIO driver. However, such a
big change, affecting 3 drivers all together, is not suitable for the rc
cycle, and should be prepared for the merge window. Then, an
alternative solution is proposed as a regression fix.
For the ams-delta NAND driver to initialize correctly in coexistence
with the changed GPIO driver, drop the request_mem_region() call from
the former, especially as this call is going to be removed while the
long-term solution is implemented.
Tested on Amstrad Delta.
Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
Acked-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2012-05-07 22:51:37 +02:00
|
|
|
goto out_free;
|
2010-12-15 15:43:44 +01:00
|
|
|
}
|
|
|
|
|
2018-09-20 00:52:54 +02:00
|
|
|
priv->io_base = io_base;
|
|
|
|
nand_set_controller_data(this, priv);
|
2010-12-15 15:43:44 +01:00
|
|
|
|
2006-05-21 18:11:55 +01:00
|
|
|
/* Set address of NAND IO lines */
|
2018-10-15 21:41:30 +02:00
|
|
|
this->select_chip = ams_delta_select_chip;
|
|
|
|
this->exec_op = ams_delta_exec_op;
|
2018-09-20 00:17:29 +02:00
|
|
|
|
2018-09-20 00:52:54 +02:00
|
|
|
priv->gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN);
|
|
|
|
if (IS_ERR(priv->gpiod_rdy)) {
|
|
|
|
err = PTR_ERR(priv->gpiod_rdy);
|
2018-09-20 00:17:29 +02:00
|
|
|
dev_warn(&pdev->dev, "RDY GPIO request failed (%d)\n", err);
|
|
|
|
goto out_mtd;
|
2006-05-21 18:11:55 +01:00
|
|
|
}
|
2018-09-20 00:17:29 +02:00
|
|
|
|
2006-05-23 12:00:46 +02:00
|
|
|
this->ecc.mode = NAND_ECC_SOFT;
|
2016-04-08 12:23:44 +02:00
|
|
|
this->ecc.algo = NAND_ECC_HAMMING;
|
2006-05-21 18:11:55 +01:00
|
|
|
|
2018-09-20 00:52:54 +02:00
|
|
|
platform_set_drvdata(pdev, priv);
|
2010-12-15 15:43:44 +01:00
|
|
|
|
2006-05-21 18:11:55 +01:00
|
|
|
/* Set chip enabled, but */
|
2018-09-20 00:52:54 +02:00
|
|
|
priv->gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_HIGH);
|
|
|
|
if (IS_ERR(priv->gpiod_nwp)) {
|
|
|
|
err = PTR_ERR(priv->gpiod_nwp);
|
2018-09-20 00:17:29 +02:00
|
|
|
dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err);
|
|
|
|
goto out_mtd;
|
|
|
|
}
|
|
|
|
|
2018-09-20 00:52:54 +02:00
|
|
|
priv->gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_HIGH);
|
|
|
|
if (IS_ERR(priv->gpiod_nce)) {
|
|
|
|
err = PTR_ERR(priv->gpiod_nce);
|
2018-09-20 00:17:29 +02:00
|
|
|
dev_err(&pdev->dev, "NCE GPIO request failed (%d)\n", err);
|
|
|
|
goto out_mtd;
|
|
|
|
}
|
|
|
|
|
2018-09-20 00:52:54 +02:00
|
|
|
priv->gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_HIGH);
|
|
|
|
if (IS_ERR(priv->gpiod_nre)) {
|
|
|
|
err = PTR_ERR(priv->gpiod_nre);
|
2018-09-20 00:17:29 +02:00
|
|
|
dev_err(&pdev->dev, "NRE GPIO request failed (%d)\n", err);
|
|
|
|
goto out_mtd;
|
|
|
|
}
|
|
|
|
|
2018-09-20 00:52:54 +02:00
|
|
|
priv->gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_HIGH);
|
|
|
|
if (IS_ERR(priv->gpiod_nwe)) {
|
|
|
|
err = PTR_ERR(priv->gpiod_nwe);
|
2018-09-20 00:17:29 +02:00
|
|
|
dev_err(&pdev->dev, "NWE GPIO request failed (%d)\n", err);
|
|
|
|
goto out_mtd;
|
|
|
|
}
|
|
|
|
|
2018-09-20 00:52:54 +02:00
|
|
|
priv->gpiod_ale = devm_gpiod_get(&pdev->dev, "ale", GPIOD_OUT_LOW);
|
|
|
|
if (IS_ERR(priv->gpiod_ale)) {
|
|
|
|
err = PTR_ERR(priv->gpiod_ale);
|
2018-09-20 00:17:29 +02:00
|
|
|
dev_err(&pdev->dev, "ALE GPIO request failed (%d)\n", err);
|
|
|
|
goto out_mtd;
|
|
|
|
}
|
|
|
|
|
2018-09-20 00:52:54 +02:00
|
|
|
priv->gpiod_cle = devm_gpiod_get(&pdev->dev, "cle", GPIOD_OUT_LOW);
|
|
|
|
if (IS_ERR(priv->gpiod_cle)) {
|
|
|
|
err = PTR_ERR(priv->gpiod_cle);
|
2018-09-20 00:17:29 +02:00
|
|
|
dev_err(&pdev->dev, "CLE GPIO request failed (%d)\n", err);
|
|
|
|
goto out_mtd;
|
|
|
|
}
|
2006-05-21 18:11:55 +01:00
|
|
|
|
2018-09-20 00:52:55 +02:00
|
|
|
/* Initialize data port direction to a known state */
|
|
|
|
ams_delta_dir_input(priv, true);
|
|
|
|
|
2011-03-30 22:57:33 -03:00
|
|
|
/* Scan to find existence of the device */
|
2018-09-06 14:05:14 +02:00
|
|
|
err = nand_scan(this, 1);
|
2016-11-04 19:42:49 +09:00
|
|
|
if (err)
|
2006-05-21 18:11:55 +01:00
|
|
|
goto out_mtd;
|
|
|
|
|
|
|
|
/* Register the partitions */
|
2018-09-20 00:52:54 +02:00
|
|
|
mtd_device_register(mtd, partition_info, ARRAY_SIZE(partition_info));
|
2006-05-21 18:11:55 +01:00
|
|
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
out_mtd:
|
2010-12-15 15:43:44 +01:00
|
|
|
iounmap(io_base);
|
|
|
|
out_free:
|
2006-05-21 18:11:55 +01:00
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clean up routine
|
|
|
|
*/
|
2012-11-19 13:26:04 -05:00
|
|
|
static int ams_delta_cleanup(struct platform_device *pdev)
|
2006-05-21 18:11:55 +01:00
|
|
|
{
|
2018-09-20 00:52:54 +02:00
|
|
|
struct ams_delta_nand *priv = platform_get_drvdata(pdev);
|
|
|
|
struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip);
|
|
|
|
void __iomem *io_base = priv->io_base;
|
2010-12-15 15:43:44 +01:00
|
|
|
|
2006-05-21 18:11:55 +01:00
|
|
|
/* Release resources, unregister device */
|
2018-09-20 00:52:54 +02:00
|
|
|
nand_release(mtd_to_nand(mtd));
|
2006-05-21 18:11:55 +01:00
|
|
|
|
2010-12-15 15:43:44 +01:00
|
|
|
iounmap(io_base);
|
|
|
|
|
2010-12-14 21:09:40 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct platform_driver ams_delta_nand_driver = {
|
|
|
|
.probe = ams_delta_init,
|
2012-11-19 13:21:24 -05:00
|
|
|
.remove = ams_delta_cleanup,
|
2010-12-14 21:09:40 +01:00
|
|
|
.driver = {
|
|
|
|
.name = "ams-delta-nand",
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2011-11-27 20:45:03 +08:00
|
|
|
module_platform_driver(ams_delta_nand_driver);
|
2006-05-21 18:11:55 +01:00
|
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_AUTHOR("Jonathan McDowell <noodles@earth.li>");
|
|
|
|
MODULE_DESCRIPTION("Glue layer for NAND flash on Amstrad E3 (Delta)");
|