Commit 699274b1 authored by Miquel Raynal's avatar Miquel Raynal
Browse files

Merge tag 'nand/for-5.7' into mtd/next

Raw NAND core changes:
* Add support for manufacturer specific suspend/resume operation
* Add support for manufacturer specific lock/unlock operation
* Replace zero-length array with flexible-array member
* Fix a typo ("manufecturer")
* Ensure nand_soft_waitrdy wait period is enough

Raw NAND controller driver changes:
* Brcmnand:
Add support for flash-edu for dma transfers (+ bindings)
* Cadence:
Reinit completion before executing a new command
Change bad block marker size
Fix the calculation of the avaialble OOB size
Get meta data size from registers
* Qualcom:
Use dma_request_chan() instead dma_request_slave_channel()
Release resources on failure within qcom_nandc_alloc()
* Allwinner:
Use dma_request_chan() instead dma_request_slave_channel()
* Marvell:
Use dma_request_chan() instead dma_request_slave_channel()
Release DMA channel on error
* Freescale:
Use dma_request_chan() instead dma_request_slave_channel()
* Macronix:
Add support for Macronix NAND randomizer (+ bindings)
* Ams-delta:
Rename structures and functions to gpio_nand*
Make the driver custom I/O ready
Drop useless local variable
Support custom driver initialisation
Add module device tables
Handle more GPIO pins as optional
Make read pulses optional
Don't hardcode read/write pulse widths
Push inversion handling to gpiolib
Enable OF partition info support
Drop board specific partition info
Use struct gpio_nand_platdata
Write protect device during probe
* Ingenic:
Use devm_platform_ioremap_resource()
Add dependency on MIPS || COMPILE_TEST
* Denali:
Deassert write protect pin
* ST:
Use dma_request_chan() instead dma_request_slave_channel()

Raw NAND chip driver changes:
* Toshiba:
Support reading the number of bitflips for BENAND (Built-in ECC NAND)
* Macronix:
Add support for deep power down mode
Add support for block protection

SPI-NAND core changes:
* Do not erase the block before writing a bad block marker
* Explicitly use MTD_OPS_RAW to write the bad block marker to OOB
* Stop using spinand->oobbuf for buffering bad block markers
* Rework detect procedure for different READ_ID operation

SPI-NAND driver changes:
* Toshiba:
Support for new Kioxia Serial NAND
Rename function name to change suffix and prefix (8Gbit)
Add comment about Kioxia ID
* Micron:
Add new Micron SPI NAND devices with multiple dies
Add M70A series Micron SPI NAND devices
identify SPI NAND device with Continuous Read mode
Add new Micron SPI NAND devices
Describe the SPI NAND device MT29F2G01ABAGD
Generalize the OOB layout structure and function names
parents 245bbe80 fca88925
Loading
Loading
Loading
Loading
+5 −5
Original line number Diff line number Diff line
@@ -35,11 +35,11 @@ Required properties:
                     (optional) NAND flash cache range (if at non-standard offset)
- reg-names        : a list of the names corresponding to the previous register
                     ranges. Should contain "nand" and (optionally)
                     "flash-dma" and/or "nand-cache".
- interrupts       : The NAND CTLRDY interrupt and (if Flash DMA is available)
                     FLASH_DMA_DONE
- interrupt-names  : May be "nand_ctlrdy" or "flash_dma_done", if broken out as
                     individual interrupts.
                     "flash-dma" or "flash-edu" and/or "nand-cache".
- interrupts       : The NAND CTLRDY interrupt, (if Flash DMA is available)
                     FLASH_DMA_DONE and if EDU is avaialble and used FLASH_EDU_DONE
- interrupt-names  : May be "nand_ctlrdy" or "flash_dma_done" or "flash_edu_done",
                     if broken out as individual interrupts.
                     May be "nand", if the SoC has the individual NAND
                     interrupts multiplexed behind another custom piece of
                     hardware
+27 −0
Original line number Diff line number Diff line
Macronix NANDs Device Tree Bindings
-----------------------------------

Macronix NANDs support randomizer operation for scrambling user data,
which can be enabled with a SET_FEATURE. The penalty when using the
randomizer are subpage accesses prohibited and more time period needed
for program operation, i.e., tPROG 300us to 340us (randomizer enabled).
Enabling the randomizer is a one time persistent and non reversible
operation.

For more high-reliability concern, if subpage write is not available
with hardware ECC and not enabled at UBI level, then enabling the
randomizer is recommended by default by adding a new specific property
in children nodes.

Required NAND chip properties in children mode:
- randomizer enable: should be "mxic,enable-randomizer-otp"

Example:

	nand: nand-controller@unit-address {

		nand@0 {
			reg = <0>;
			mxic,enable-randomizer-otp;
		};
	};
+43 −4
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/leds.h>
#include <linux/mtd/nand-gpio.h>
#include <linux/mtd/partitions.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/regulator/fixed.h>
@@ -294,9 +296,42 @@ struct modem_private_data {

static struct modem_private_data modem_priv;

/*
 * Define partitions for flash device
 */

static struct mtd_partition partition_info[] = {
	{ .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 },
};

static struct gpio_nand_platdata nand_platdata = {
	.parts		= partition_info,
	.num_parts	= ARRAY_SIZE(partition_info),
};

static struct platform_device ams_delta_nand_device = {
	.name	= "ams-delta-nand",
	.id	= -1,
	.dev	= {
		.platform_data = &nand_platdata,
	},
};

#define OMAP_GPIO_LABEL		"gpio-0-15"
@@ -306,10 +341,14 @@ static struct gpiod_lookup_table ams_delta_nand_gpio_table = {
	.table = {
		GPIO_LOOKUP(OMAP_GPIO_LABEL, AMS_DELTA_GPIO_PIN_NAND_RB, "rdy",
			    0),
		GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NCE, "nce", 0),
		GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NRE, "nre", 0),
		GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NWP, "nwp", 0),
		GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NWE, "nwe", 0),
		GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NCE, "nce",
			    GPIO_ACTIVE_LOW),
		GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NRE, "nre",
			    GPIO_ACTIVE_LOW),
		GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NWP, "nwp",
			    GPIO_ACTIVE_LOW),
		GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NWE, "nwe",
			    GPIO_ACTIVE_LOW),
		GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_ALE, "ale", 0),
		GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_CLE, "cle", 0),
		GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 0, "data", 0, 0),
+2 −2
Original line number Diff line number Diff line
@@ -403,8 +403,8 @@
			compatible = "brcm,brcmnand-v5.0", "brcm,brcmnand";
			#address-cells = <1>;
			#size-cells = <0>;
			reg-names = "nand";
			reg = <0x41b800 0x400>;
			reg-names = "nand", "flash-edu";
			reg = <0x41b800 0x400>, <0x41bc00 0x24>;
			interrupt-parent = <&hif_l2_intc>;
			interrupts = <24>;
			status = "disabled";
+155 −82
Original line number Diff line number Diff line
@@ -19,15 +19,17 @@
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand-gpio.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/sizes.h>

/*
 * MTD structure for E3 (Delta)
 */
struct ams_delta_nand {
struct gpio_nand {
	struct nand_controller	base;
	struct nand_chip	nand_chip;
	struct gpio_desc	*gpiod_rdy;
@@ -39,41 +41,20 @@ struct ams_delta_nand {
	struct gpio_desc	*gpiod_cle;
	struct gpio_descs	*data_gpiods;
	bool			data_in;
	unsigned int		tRP;
	unsigned int		tWP;
	u8			(*io_read)(struct gpio_nand *this);
	void			(*io_write)(struct gpio_nand *this, u8 byte);
};

/*
 * Define partitions for flash devices
 */

static const struct mtd_partition partition_info[] = {
	{ .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 },
};

static void ams_delta_write_commit(struct ams_delta_nand *priv)
static void gpio_nand_write_commit(struct gpio_nand *priv)
{
	gpiod_set_value(priv->gpiod_nwe, 0);
	ndelay(40);
	gpiod_set_value(priv->gpiod_nwe, 1);
	ndelay(priv->tWP);
	gpiod_set_value(priv->gpiod_nwe, 0);
}

static void ams_delta_io_write(struct ams_delta_nand *priv, u8 byte)
static void gpio_nand_io_write(struct gpio_nand *priv, u8 byte)
{
	struct gpio_descs *data_gpiods = priv->data_gpiods;
	DECLARE_BITMAP(values, BITS_PER_TYPE(byte)) = { byte, };
@@ -81,10 +62,10 @@ static void ams_delta_io_write(struct ams_delta_nand *priv, u8 byte)
	gpiod_set_raw_array_value(data_gpiods->ndescs, data_gpiods->desc,
				  data_gpiods->info, values);

	ams_delta_write_commit(priv);
	gpio_nand_write_commit(priv);
}

static void ams_delta_dir_output(struct ams_delta_nand *priv, u8 byte)
static void gpio_nand_dir_output(struct gpio_nand *priv, u8 byte)
{
	struct gpio_descs *data_gpiods = priv->data_gpiods;
	DECLARE_BITMAP(values, BITS_PER_TYPE(byte)) = { byte, };
@@ -94,30 +75,30 @@ static void ams_delta_dir_output(struct ams_delta_nand *priv, u8 byte)
		gpiod_direction_output_raw(data_gpiods->desc[i],
					   test_bit(i, values));

	ams_delta_write_commit(priv);
	gpio_nand_write_commit(priv);

	priv->data_in = false;
}

static u8 ams_delta_io_read(struct ams_delta_nand *priv)
static u8 gpio_nand_io_read(struct gpio_nand *priv)
{
	u8 res;
	struct gpio_descs *data_gpiods = priv->data_gpiods;
	DECLARE_BITMAP(values, BITS_PER_TYPE(res)) = { 0, };

	gpiod_set_value(priv->gpiod_nre, 0);
	ndelay(40);
	gpiod_set_value(priv->gpiod_nre, 1);
	ndelay(priv->tRP);

	gpiod_get_raw_array_value(data_gpiods->ndescs, data_gpiods->desc,
				  data_gpiods->info, values);

	gpiod_set_value(priv->gpiod_nre, 1);
	gpiod_set_value(priv->gpiod_nre, 0);

	res = values[0];
	return res;
}

static void ams_delta_dir_input(struct ams_delta_nand *priv)
static void gpio_nand_dir_input(struct gpio_nand *priv)
{
	struct gpio_descs *data_gpiods = priv->data_gpiods;
	int i;
@@ -128,68 +109,67 @@ static void ams_delta_dir_input(struct ams_delta_nand *priv)
	priv->data_in = true;
}

static void ams_delta_write_buf(struct ams_delta_nand *priv, const u8 *buf,
				int len)
static void gpio_nand_write_buf(struct gpio_nand *priv, const u8 *buf, int len)
{
	int i = 0;

	if (len > 0 && priv->data_in)
		ams_delta_dir_output(priv, buf[i++]);
		gpio_nand_dir_output(priv, buf[i++]);

	while (i < len)
		ams_delta_io_write(priv, buf[i++]);
		priv->io_write(priv, buf[i++]);
}

static void ams_delta_read_buf(struct ams_delta_nand *priv, u8 *buf, int len)
static void gpio_nand_read_buf(struct gpio_nand *priv, u8 *buf, int len)
{
	int i;

	if (!priv->data_in)
		ams_delta_dir_input(priv);
	if (priv->data_gpiods && !priv->data_in)
		gpio_nand_dir_input(priv);

	for (i = 0; i < len; i++)
		buf[i] = ams_delta_io_read(priv);
		buf[i] = priv->io_read(priv);
}

static void ams_delta_ctrl_cs(struct ams_delta_nand *priv, bool assert)
static void gpio_nand_ctrl_cs(struct gpio_nand *priv, bool assert)
{
	gpiod_set_value(priv->gpiod_nce, assert ? 0 : 1);
	gpiod_set_value(priv->gpiod_nce, assert);
}

static int ams_delta_exec_op(struct nand_chip *this,
static int gpio_nand_exec_op(struct nand_chip *this,
			     const struct nand_operation *op, bool check_only)
{
	struct ams_delta_nand *priv = nand_get_controller_data(this);
	struct gpio_nand *priv = nand_get_controller_data(this);
	const struct nand_op_instr *instr;
	int ret = 0;

	if (check_only)
		return 0;

	ams_delta_ctrl_cs(priv, 1);
	gpio_nand_ctrl_cs(priv, 1);

	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);
			gpio_nand_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,
			gpio_nand_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,
			gpio_nand_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,
			gpio_nand_write_buf(priv, instr->ctx.data.buf.out,
					    instr->ctx.data.len);
			break;

@@ -206,28 +186,61 @@ static int ams_delta_exec_op(struct nand_chip *this,
			break;
	}

	ams_delta_ctrl_cs(priv, 0);
	gpio_nand_ctrl_cs(priv, 0);

	return ret;
}

static const struct nand_controller_ops ams_delta_ops = {
	.exec_op = ams_delta_exec_op,
static int gpio_nand_setup_data_interface(struct nand_chip *this, int csline,
					  const struct nand_data_interface *cf)
{
	struct gpio_nand *priv = nand_get_controller_data(this);
	const struct nand_sdr_timings *sdr = nand_get_sdr_timings(cf);
	struct device *dev = &nand_to_mtd(this)->dev;

	if (IS_ERR(sdr))
		return PTR_ERR(sdr);

	if (csline == NAND_DATA_IFACE_CHECK_ONLY)
		return 0;

	if (priv->gpiod_nre) {
		priv->tRP = DIV_ROUND_UP(sdr->tRP_min, 1000);
		dev_dbg(dev, "using %u ns read pulse width\n", priv->tRP);
	}

	priv->tWP = DIV_ROUND_UP(sdr->tWP_min, 1000);
	dev_dbg(dev, "using %u ns write pulse width\n", priv->tWP);

	return 0;
}

static const struct nand_controller_ops gpio_nand_ops = {
	.exec_op = gpio_nand_exec_op,
	.setup_data_interface = gpio_nand_setup_data_interface,
};

/*
 * Main initialization routine
 */
static int ams_delta_init(struct platform_device *pdev)
static int gpio_nand_probe(struct platform_device *pdev)
{
	struct ams_delta_nand *priv;
	struct gpio_nand_platdata *pdata = dev_get_platdata(&pdev->dev);
	const struct mtd_partition *partitions = NULL;
	int num_partitions = 0;
	struct gpio_nand *priv;
	struct nand_chip *this;
	struct mtd_info *mtd;
	struct gpio_descs *data_gpiods;
	int (*probe)(struct platform_device *pdev, struct gpio_nand *priv);
	int err = 0;

	if (pdata) {
		partitions = pdata->parts;
		num_partitions = pdata->num_parts;
	}

	/* Allocate memory for MTD device structure and private data */
	priv = devm_kzalloc(&pdev->dev, sizeof(struct ams_delta_nand),
	priv = devm_kzalloc(&pdev->dev, sizeof(struct gpio_nand),
			    GFP_KERNEL);
	if (!priv)
		return -ENOMEM;
@@ -238,6 +251,7 @@ static int ams_delta_init(struct platform_device *pdev)
	mtd->dev.parent = &pdev->dev;

	nand_set_controller_data(this, priv);
	nand_set_flash_node(this, pdev->dev.of_node);

	priv->gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN);
	if (IS_ERR(priv->gpiod_rdy)) {
@@ -251,29 +265,33 @@ static int ams_delta_init(struct platform_device *pdev)

	platform_set_drvdata(pdev, priv);

	/* Set chip enabled, but  */
	priv->gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_HIGH);
	/* Set chip enabled but write protected */
	priv->gpiod_nwp = devm_gpiod_get_optional(&pdev->dev, "nwp",
						  GPIOD_OUT_HIGH);
	if (IS_ERR(priv->gpiod_nwp)) {
		err = PTR_ERR(priv->gpiod_nwp);
		dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err);
		return err;
	}

	priv->gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_HIGH);
	priv->gpiod_nce = devm_gpiod_get_optional(&pdev->dev, "nce",
						  GPIOD_OUT_LOW);
	if (IS_ERR(priv->gpiod_nce)) {
		err = PTR_ERR(priv->gpiod_nce);
		dev_err(&pdev->dev, "NCE GPIO request failed (%d)\n", err);
		return err;
	}

	priv->gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_HIGH);
	priv->gpiod_nre = devm_gpiod_get_optional(&pdev->dev, "nre",
						  GPIOD_OUT_LOW);
	if (IS_ERR(priv->gpiod_nre)) {
		err = PTR_ERR(priv->gpiod_nre);
		dev_err(&pdev->dev, "NRE GPIO request failed (%d)\n", err);
		return err;
	}

	priv->gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_HIGH);
	priv->gpiod_nwe = devm_gpiod_get_optional(&pdev->dev, "nwe",
						  GPIOD_OUT_LOW);
	if (IS_ERR(priv->gpiod_nwe)) {
		err = PTR_ERR(priv->gpiod_nwe);
		dev_err(&pdev->dev, "NWE GPIO request failed (%d)\n", err);
@@ -295,28 +313,62 @@ static int ams_delta_init(struct platform_device *pdev)
	}

	/* Request array of data pins, initialize them as input */
	data_gpiods = devm_gpiod_get_array(&pdev->dev, "data", GPIOD_IN);
	if (IS_ERR(data_gpiods)) {
		err = PTR_ERR(data_gpiods);
	priv->data_gpiods = devm_gpiod_get_array_optional(&pdev->dev, "data",
							  GPIOD_IN);
	if (IS_ERR(priv->data_gpiods)) {
		err = PTR_ERR(priv->data_gpiods);
		dev_err(&pdev->dev, "data GPIO request failed: %d\n", err);
		return err;
	}
	priv->data_gpiods = data_gpiods;
	if (priv->data_gpiods) {
		if (!priv->gpiod_nwe) {
			dev_err(&pdev->dev,
				"mandatory NWE pin not provided by platform\n");
			return -ENODEV;
		}

		priv->io_read = gpio_nand_io_read;
		priv->io_write = gpio_nand_io_write;
		priv->data_in = true;
	}

	if (pdev->id_entry)
		probe = (void *) pdev->id_entry->driver_data;
	else
		probe = of_device_get_match_data(&pdev->dev);
	if (probe)
		err = probe(pdev, priv);
	if (err)
		return err;

	if (!priv->io_read || !priv->io_write) {
		dev_err(&pdev->dev, "incomplete device configuration\n");
		return -ENODEV;
	}

	/* Initialize the NAND controller object embedded in ams_delta_nand. */
	priv->base.ops = &ams_delta_ops;
	/* Initialize the NAND controller object embedded in gpio_nand. */
	priv->base.ops = &gpio_nand_ops;
	nand_controller_init(&priv->base);
	this->controller = &priv->base;

	/*
	 * FIXME: We should release write protection only after nand_scan() to
	 * be on the safe side but we can't do that until we have a generic way
	 * to assert/deassert WP from the core.  Even if the core shouldn't
	 * write things in the nand_scan() path, it should have control on this
	 * pin just in case we ever need to disable write protection during
	 * chip detection/initialization.
	 */
	/* Release write protection */
	gpiod_set_value(priv->gpiod_nwp, 0);

	/* Scan to find existence of the device */
	err = nand_scan(this, 1);
	if (err)
		return err;

	/* Register the partitions */
	err = mtd_device_register(mtd, partition_info,
				  ARRAY_SIZE(partition_info));
	err = mtd_device_register(mtd, partitions, num_partitions);
	if (err)
		goto err_nand_cleanup;

@@ -331,26 +383,47 @@ err_nand_cleanup:
/*
 * Clean up routine
 */
static int ams_delta_cleanup(struct platform_device *pdev)
static int gpio_nand_remove(struct platform_device *pdev)
{
	struct ams_delta_nand *priv = platform_get_drvdata(pdev);
	struct gpio_nand *priv = platform_get_drvdata(pdev);
	struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip);

	/* Apply write protection */
	gpiod_set_value(priv->gpiod_nwp, 1);

	/* Unregister device */
	nand_release(mtd_to_nand(mtd));

	return 0;
}

static struct platform_driver ams_delta_nand_driver = {
	.probe		= ams_delta_init,
	.remove		= ams_delta_cleanup,
static const struct of_device_id gpio_nand_of_id_table[] = {
	{
		/* sentinel */
	},
};
MODULE_DEVICE_TABLE(of, gpio_nand_of_id_table);

static const struct platform_device_id gpio_nand_plat_id_table[] = {
	{
		.name	= "ams-delta-nand",
	}, {
		/* sentinel */
	},
};
MODULE_DEVICE_TABLE(platform, gpio_nand_plat_id_table);

static struct platform_driver gpio_nand_driver = {
	.probe		= gpio_nand_probe,
	.remove		= gpio_nand_remove,
	.id_table	= gpio_nand_plat_id_table,
	.driver		= {
		.name	= "ams-delta-nand",
		.of_match_table = of_match_ptr(gpio_nand_of_id_table),
	},
};

module_platform_driver(ams_delta_nand_driver);
module_platform_driver(gpio_nand_driver);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Jonathan McDowell <noodles@earth.li>");
Loading