Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e10db1f0 authored by Huang Shijie's avatar Huang Shijie Committed by David Woodhouse
Browse files

mtd: gpmi: add device tree support to gpmi-nand



This patch just adds the DT support to gpmi-nand.

Signed-off-by: default avatarHuang Shijie <b32955@freescale.com>
Signed-off-by: default avatarHuang Shijie <shijie8@gmail.com>
Signed-off-by: default avatarArtem Bityutskiy <artem.bityutskiy@linux.intel.com>
Signed-off-by: default avatarDavid Woodhouse <David.Woodhouse@intel.com>
parent 279f08d4
Loading
Loading
Loading
Loading
+33 −0
Original line number Diff line number Diff line
* Freescale General-Purpose Media Interface (GPMI)

The GPMI nand controller provides an interface to control the
NAND flash chips. We support only one NAND chip now.

Required properties:
  - compatible : should be "fsl,<chip>-gpmi-nand"
  - reg : should contain registers location and length for gpmi and bch.
  - reg-names: Should contain the reg names "gpmi-nand" and "bch"
  - interrupts : The first is the DMA interrupt number for GPMI.
                 The second is the BCH interrupt number.
  - interrupt-names : The interrupt names "gpmi-dma", "bch";
  - fsl,gpmi-dma-channel : Should contain the dma channel it uses.

The device tree may optionally contain sub-nodes describing partitions of the
address space. See partition.txt for more detail.

Examples:

gpmi-nand@8000c000 {
	compatible = "fsl,imx28-gpmi-nand";
	#address-cells = <1>;
	#size-cells = <1>;
	reg = <0x8000c000 2000>, <0x8000a000 2000>;
	reg-names = "gpmi-nand", "bch";
	interrupts = <88>, <41>;
	interrupt-names = "gpmi-dma", "bch";
	fsl,gpmi-dma-channel = <4>;

	partition@0 {
	...
	};
};
+4 −3
Original line number Diff line number Diff line
@@ -256,11 +256,12 @@ static unsigned int ns_to_cycles(unsigned int time,
	return max(k, min);
}

#define DEF_MIN_PROP_DELAY	5
#define DEF_MAX_PROP_DELAY	9
/* Apply timing to current hardware conditions. */
static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this,
					struct gpmi_nfc_hardware_timing *hw)
{
	struct gpmi_nand_platform_data *pdata = this->pdata;
	struct timing_threshod *nfc = &timing_default_threshold;
	struct nand_chip *nand = &this->nand;
	struct nand_timing target = this->timing;
@@ -277,8 +278,8 @@ static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this,
	int ideal_sample_delay_in_ns;
	unsigned int sample_delay_factor;
	int tEYE;
	unsigned int min_prop_delay_in_ns = pdata->min_prop_delay_in_ns;
	unsigned int max_prop_delay_in_ns = pdata->max_prop_delay_in_ns;
	unsigned int min_prop_delay_in_ns = DEF_MIN_PROP_DELAY;
	unsigned int max_prop_delay_in_ns = DEF_MAX_PROP_DELAY;

	/*
	 * If there are multiple chips, we need to relax the timings to allow
+62 −64
Original line number Diff line number Diff line
@@ -24,6 +24,8 @@
#include <linux/module.h>
#include <linux/mtd/gpmi-nand.h>
#include <linux/mtd/partitions.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include "gpmi-nand.h"

/* add our owner bbt descriptor */
@@ -386,7 +388,7 @@ static void release_bch_irq(struct gpmi_nand_data *this)
static bool gpmi_dma_filter(struct dma_chan *chan, void *param)
{
	struct gpmi_nand_data *this = param;
	struct resource *r = this->private;
	int dma_channel = (int)this->private;

	if (!mxs_dma_is_apbh(chan))
		return false;
@@ -398,7 +400,7 @@ static bool gpmi_dma_filter(struct dma_chan *chan, void *param)
	 *	for mx28 :	MX28_DMA_GPMI0 ~ MX28_DMA_GPMI7
	 *		(These eight channels share the same IRQ!)
	 */
	if (r->start <= chan->chan_id && chan->chan_id <= r->end) {
	if (dma_channel == chan->chan_id) {
		chan->private = &this->dma_data;
		return true;
	}
@@ -418,57 +420,45 @@ static void release_dma_channels(struct gpmi_nand_data *this)
static int __devinit acquire_dma_channels(struct gpmi_nand_data *this)
{
	struct platform_device *pdev = this->pdev;
	struct gpmi_nand_platform_data *pdata = this->pdata;
	struct resources *res = &this->resources;
	struct resource *r, *r_dma;
	unsigned int i;
	struct resource *r_dma;
	struct device_node *dn;
	int dma_channel;
	unsigned int ret;
	struct dma_chan *dma_chan;
	dma_cap_mask_t mask;

	/* dma channel, we only use the first one. */
	dn = pdev->dev.of_node;
	ret = of_property_read_u32(dn, "fsl,gpmi-dma-channel", &dma_channel);
	if (ret) {
		pr_err("unable to get DMA channel from dt.\n");
		goto acquire_err;
	}
	this->private = (void *)dma_channel;

	r = platform_get_resource_byname(pdev, IORESOURCE_DMA,
					GPMI_NAND_DMA_CHANNELS_RES_NAME);
	/* gpmi dma interrupt */
	r_dma = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
					GPMI_NAND_DMA_INTERRUPT_RES_NAME);
	if (!r || !r_dma) {
	if (!r_dma) {
		pr_err("Can't get resource for DMA\n");
		return -ENXIO;
		goto acquire_err;
	}
	this->dma_data.chan_irq = r_dma->start;

	/* used in gpmi_dma_filter() */
	this->private = r;

	for (i = r->start; i <= r->end; i++) {
		struct dma_chan *dma_chan;
		dma_cap_mask_t mask;

		if (i - r->start >= pdata->max_chip_count)
			break;

	/* request dma channel */
	dma_cap_zero(mask);
	dma_cap_set(DMA_SLAVE, mask);

		/* get the DMA interrupt */
		if (r_dma->start == r_dma->end) {
			/* only register the first. */
			if (i == r->start)
				this->dma_data.chan_irq = r_dma->start;
			else
				this->dma_data.chan_irq = NO_IRQ;
		} else
			this->dma_data.chan_irq = r_dma->start + (i - r->start);

	dma_chan = dma_request_channel(mask, gpmi_dma_filter, this);
		if (!dma_chan)
	if (!dma_chan) {
		pr_err("dma_request_channel failed.\n");
		goto acquire_err;

		/* fill the first empty item */
		this->dma_chans[i - r->start] = dma_chan;
	}

	res->dma_low_channel = r->start;
	res->dma_high_channel = i;
	this->dma_chans[0] = dma_chan;
	return 0;

acquire_err:
	pr_err("Can't acquire DMA channel %u\n", i);
	release_dma_channels(this);
	return -EINVAL;
}
@@ -1465,9 +1455,9 @@ void gpmi_nfc_exit(struct gpmi_nand_data *this)

static int __devinit gpmi_nfc_init(struct gpmi_nand_data *this)
{
	struct gpmi_nand_platform_data *pdata = this->pdata;
	struct mtd_info  *mtd = &this->mtd;
	struct nand_chip *chip = &this->nand;
	struct mtd_part_parser_data ppdata = {};
	int ret;

	/* init current chip */
@@ -1505,14 +1495,14 @@ static int __devinit gpmi_nfc_init(struct gpmi_nand_data *this)
	if (ret)
		goto err_out;

	ret = nand_scan(mtd, pdata->max_chip_count);
	ret = nand_scan(mtd, 1);
	if (ret) {
		pr_err("Chip scan failed\n");
		goto err_out;
	}

	ret = mtd_device_parse_register(mtd, NULL, NULL,
			pdata->partitions, pdata->partition_count);
	ppdata.of_node = this->pdev->dev.of_node;
	ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
	if (ret)
		goto err_out;
	return 0;
@@ -1522,12 +1512,37 @@ static int __devinit gpmi_nfc_init(struct gpmi_nand_data *this)
	return ret;
}

static const struct platform_device_id gpmi_ids[] = {
	{ .name = "imx23-gpmi-nand", .driver_data = IS_MX23, },
	{ .name = "imx28-gpmi-nand", .driver_data = IS_MX28, },
	{},
};

static const struct of_device_id gpmi_nand_id_table[] = {
	{
		.compatible = "fsl,imx23-gpmi-nand",
		.data = (void *)&gpmi_ids[IS_MX23]
	}, {
		.compatible = "fsl,imx28-gpmi-nand",
		.data = (void *)&gpmi_ids[IS_MX28]
	}, {}
};
MODULE_DEVICE_TABLE(of, gpmi_nand_id_table);

static int __devinit gpmi_nand_probe(struct platform_device *pdev)
{
	struct gpmi_nand_platform_data *pdata = pdev->dev.platform_data;
	struct gpmi_nand_data *this;
	const struct of_device_id *of_id;
	int ret;

	of_id = of_match_device(gpmi_nand_id_table, &pdev->dev);
	if (of_id) {
		pdev->id_entry = of_id->data;
	} else {
		pr_err("Failed to find the right device id.\n");
		return -ENOMEM;
	}

	this = kzalloc(sizeof(*this), GFP_KERNEL);
	if (!this) {
		pr_err("Failed to allocate per-device memory\n");
@@ -1537,13 +1552,6 @@ static int __devinit gpmi_nand_probe(struct platform_device *pdev)
	platform_set_drvdata(pdev, this);
	this->pdev  = pdev;
	this->dev   = &pdev->dev;
	this->pdata = pdata;

	if (pdata->platform_init) {
		ret = pdata->platform_init();
		if (ret)
			goto platform_init_error;
	}

	ret = acquire_resources(this);
	if (ret)
@@ -1561,7 +1569,6 @@ static int __devinit gpmi_nand_probe(struct platform_device *pdev)

exit_nfc_init:
	release_resources(this);
platform_init_error:
exit_acquire_resources:
	platform_set_drvdata(pdev, NULL);
	kfree(this);
@@ -1579,19 +1586,10 @@ static int __exit gpmi_nand_remove(struct platform_device *pdev)
	return 0;
}

static const struct platform_device_id gpmi_ids[] = {
	{
		.name = "imx23-gpmi-nand",
		.driver_data = IS_MX23,
	}, {
		.name = "imx28-gpmi-nand",
		.driver_data = IS_MX28,
	}, {},
};

static struct platform_driver gpmi_nand_driver = {
	.driver = {
		.name = "gpmi-nand",
		.of_match_table = gpmi_nand_id_table,
	},
	.probe   = gpmi_nand_probe,
	.remove  = __exit_p(gpmi_nand_remove),
+2 −2
Original line number Diff line number Diff line
@@ -266,8 +266,8 @@ extern int gpmi_read_page(struct gpmi_nand_data *,
#define STATUS_UNCORRECTABLE	0xfe

/* Use the platform_id to distinguish different Archs. */
#define IS_MX23			0x1
#define IS_MX28			0x2
#define IS_MX23			0x0
#define IS_MX28			0x1
#define GPMI_IS_MX23(x)		((x)->pdev->id_entry->driver_data == IS_MX23)
#define GPMI_IS_MX28(x)		((x)->pdev->id_entry->driver_data == IS_MX28)
#endif
+4 −4
Original line number Diff line number Diff line
@@ -23,12 +23,12 @@
#define GPMI_NAND_RES_SIZE	6

/* Resource names for the GPMI NAND driver. */
#define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME  "GPMI NAND GPMI Registers"
#define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME  "gpmi-nand"
#define GPMI_NAND_GPMI_INTERRUPT_RES_NAME  "GPMI NAND GPMI Interrupt"
#define GPMI_NAND_BCH_REGS_ADDR_RES_NAME   "GPMI NAND BCH Registers"
#define GPMI_NAND_BCH_INTERRUPT_RES_NAME   "GPMI NAND BCH Interrupt"
#define GPMI_NAND_BCH_REGS_ADDR_RES_NAME   "bch"
#define GPMI_NAND_BCH_INTERRUPT_RES_NAME   "bch"
#define GPMI_NAND_DMA_CHANNELS_RES_NAME    "GPMI NAND DMA Channels"
#define GPMI_NAND_DMA_INTERRUPT_RES_NAME   "GPMI NAND DMA Interrupt"
#define GPMI_NAND_DMA_INTERRUPT_RES_NAME   "gpmi-dma"

/**
 * struct gpmi_nand_platform_data - GPMI NAND driver platform data.