mtd: gpmi: add device tree support to gpmi-nand
This patch just adds the DT support to gpmi-nand. Signed-off-by: Huang Shijie <b32955@freescale.com> Signed-off-by: Huang Shijie <shijie8@gmail.com> Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
This commit is contained in:
parent
279f08d4ef
commit
e10db1f00a
5 changed files with 108 additions and 76 deletions
33
Documentation/devicetree/bindings/mtd/gpmi-nand.txt
Normal file
33
Documentation/devicetree/bindings/mtd/gpmi-nand.txt
Normal file
|
@ -0,0 +1,33 @@
|
|||
* 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 {
|
||||
...
|
||||
};
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
r = platform_get_resource_byname(pdev, IORESOURCE_DMA,
|
||||
GPMI_NAND_DMA_CHANNELS_RES_NAME);
|
||||
/* 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;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* request dma channel */
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
dma_chan = dma_request_channel(mask, gpmi_dma_filter, this);
|
||||
if (!dma_chan) {
|
||||
pr_err("dma_request_channel failed.\n");
|
||||
goto acquire_err;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
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)
|
||||
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),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue