mtd: gpio-nand: add device tree bindings
Add device tree bindings so that the gpio-nand driver may be instantiated from the device tree. This also allows the partitions to be specified in the device tree. v7: - restore runtime device tree/non device tree detection v6: - convert to mtd_device_parse_register() v5: - fold dt config helpers into a single gpio_nand_of_get_config() v4: - get io sync address from gpio-control-nand,io-sync-reg property rather than a resource - clarified a few details in the binding v3: - remove redundant cast and a couple of whitespace/naming changes v2: - add CONFIG_OF guards for non-dt platforms - compatible becomes gpio-control-nand - clarify some binding details Signed-off-by: Jamie Iles <jamie@jamieiles.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
f98872fc14
commit
775c322087
2 changed files with 152 additions and 7 deletions
44
Documentation/devicetree/bindings/mtd/gpio-control-nand.txt
Normal file
44
Documentation/devicetree/bindings/mtd/gpio-control-nand.txt
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
GPIO assisted NAND flash
|
||||||
|
|
||||||
|
The GPIO assisted NAND flash uses a memory mapped interface to
|
||||||
|
read/write the NAND commands and data and GPIO pins for the control
|
||||||
|
signals.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible : "gpio-control-nand"
|
||||||
|
- reg : should specify localbus chip select and size used for the chip. The
|
||||||
|
resource describes the data bus connected to the NAND flash and all accesses
|
||||||
|
are made in native endianness.
|
||||||
|
- #address-cells, #size-cells : Must be present if the device has sub-nodes
|
||||||
|
representing partitions.
|
||||||
|
- gpios : specifies the gpio pins to control the NAND device. nwp is an
|
||||||
|
optional gpio and may be set to 0 if not present.
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- bank-width : Width (in bytes) of the device. If not present, the width
|
||||||
|
defaults to 1 byte.
|
||||||
|
- chip-delay : chip dependent delay for transferring data from array to
|
||||||
|
read registers (tR). If not present then a default of 20us is used.
|
||||||
|
- gpio-control-nand,io-sync-reg : A 64-bit physical address for a read
|
||||||
|
location used to guard against bus reordering with regards to accesses to
|
||||||
|
the GPIO's and the NAND flash data bus. If present, then after changing
|
||||||
|
GPIO state and before and after command byte writes, this register will be
|
||||||
|
read to ensure that the GPIO accesses have completed.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
gpio-nand@1,0 {
|
||||||
|
compatible = "gpio-control-nand";
|
||||||
|
reg = <1 0x0000 0x2>;
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
gpios = <&banka 1 0 /* rdy */
|
||||||
|
&banka 2 0 /* nce */
|
||||||
|
&banka 3 0 /* ale */
|
||||||
|
&banka 4 0 /* cle */
|
||||||
|
0 /* nwp */>;
|
||||||
|
|
||||||
|
partition@0 {
|
||||||
|
...
|
||||||
|
};
|
||||||
|
};
|
|
@ -27,6 +27,9 @@
|
||||||
#include <linux/mtd/nand.h>
|
#include <linux/mtd/nand.h>
|
||||||
#include <linux/mtd/partitions.h>
|
#include <linux/mtd/partitions.h>
|
||||||
#include <linux/mtd/nand-gpio.h>
|
#include <linux/mtd/nand-gpio.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
|
#include <linux/of_gpio.h>
|
||||||
|
|
||||||
struct gpiomtd {
|
struct gpiomtd {
|
||||||
void __iomem *io_sync;
|
void __iomem *io_sync;
|
||||||
|
@ -171,6 +174,96 @@ static int gpio_nand_devready(struct mtd_info *mtd)
|
||||||
return gpio_get_value(gpiomtd->plat.gpio_rdy);
|
return gpio_get_value(gpiomtd->plat.gpio_rdy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_OF
|
||||||
|
static const struct of_device_id gpio_nand_id_table[] = {
|
||||||
|
{ .compatible = "gpio-control-nand" },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, gpio_nand_id_table);
|
||||||
|
|
||||||
|
static int gpio_nand_get_config_of(const struct device *dev,
|
||||||
|
struct gpio_nand_platdata *plat)
|
||||||
|
{
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
if (!of_property_read_u32(dev->of_node, "bank-width", &val)) {
|
||||||
|
if (val == 2) {
|
||||||
|
plat->options |= NAND_BUSWIDTH_16;
|
||||||
|
} else if (val != 1) {
|
||||||
|
dev_err(dev, "invalid bank-width %u\n", val);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plat->gpio_rdy = of_get_gpio(dev->of_node, 0);
|
||||||
|
plat->gpio_nce = of_get_gpio(dev->of_node, 1);
|
||||||
|
plat->gpio_ale = of_get_gpio(dev->of_node, 2);
|
||||||
|
plat->gpio_cle = of_get_gpio(dev->of_node, 3);
|
||||||
|
plat->gpio_nwp = of_get_gpio(dev->of_node, 4);
|
||||||
|
|
||||||
|
if (!of_property_read_u32(dev->of_node, "chip-delay", &val))
|
||||||
|
plat->chip_delay = val;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct resource *gpio_nand_get_io_sync_of(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct resource *r = devm_kzalloc(&pdev->dev, sizeof(*r), GFP_KERNEL);
|
||||||
|
u64 addr;
|
||||||
|
|
||||||
|
if (!r || of_property_read_u64(pdev->dev.of_node,
|
||||||
|
"gpio-control-nand,io-sync-reg", &addr))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
r->start = addr;
|
||||||
|
r->end = r->start + 0x3;
|
||||||
|
r->flags = IORESOURCE_MEM;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
#else /* CONFIG_OF */
|
||||||
|
#define gpio_nand_id_table NULL
|
||||||
|
static inline int gpio_nand_get_config_of(const struct device *dev,
|
||||||
|
struct gpio_nand_platdata *plat)
|
||||||
|
{
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct resource *
|
||||||
|
gpio_nand_get_io_sync_of(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_OF */
|
||||||
|
|
||||||
|
static inline int gpio_nand_get_config(const struct device *dev,
|
||||||
|
struct gpio_nand_platdata *plat)
|
||||||
|
{
|
||||||
|
int ret = gpio_nand_get_config_of(dev, plat);
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (dev->platform_data) {
|
||||||
|
memcpy(plat, dev->platform_data, sizeof(*plat));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct resource *
|
||||||
|
gpio_nand_get_io_sync(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct resource *r = gpio_nand_get_io_sync_of(pdev);
|
||||||
|
|
||||||
|
if (r)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||||
|
}
|
||||||
|
|
||||||
static int __devexit gpio_nand_remove(struct platform_device *dev)
|
static int __devexit gpio_nand_remove(struct platform_device *dev)
|
||||||
{
|
{
|
||||||
struct gpiomtd *gpiomtd = platform_get_drvdata(dev);
|
struct gpiomtd *gpiomtd = platform_get_drvdata(dev);
|
||||||
|
@ -178,7 +271,7 @@ static int __devexit gpio_nand_remove(struct platform_device *dev)
|
||||||
|
|
||||||
nand_release(&gpiomtd->mtd_info);
|
nand_release(&gpiomtd->mtd_info);
|
||||||
|
|
||||||
res = platform_get_resource(dev, IORESOURCE_MEM, 1);
|
res = gpio_nand_get_io_sync(dev);
|
||||||
iounmap(gpiomtd->io_sync);
|
iounmap(gpiomtd->io_sync);
|
||||||
if (res)
|
if (res)
|
||||||
release_mem_region(res->start, resource_size(res));
|
release_mem_region(res->start, resource_size(res));
|
||||||
|
@ -226,9 +319,10 @@ static int __devinit gpio_nand_probe(struct platform_device *dev)
|
||||||
struct gpiomtd *gpiomtd;
|
struct gpiomtd *gpiomtd;
|
||||||
struct nand_chip *this;
|
struct nand_chip *this;
|
||||||
struct resource *res0, *res1;
|
struct resource *res0, *res1;
|
||||||
int ret;
|
struct mtd_part_parser_data ppdata = {};
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
if (!dev->dev.platform_data)
|
if (!dev->dev.of_node && !dev->dev.platform_data)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
res0 = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
res0 = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
||||||
|
@ -248,7 +342,7 @@ static int __devinit gpio_nand_probe(struct platform_device *dev)
|
||||||
goto err_map;
|
goto err_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
res1 = platform_get_resource(dev, IORESOURCE_MEM, 1);
|
res1 = gpio_nand_get_io_sync(dev);
|
||||||
if (res1) {
|
if (res1) {
|
||||||
gpiomtd->io_sync = request_and_remap(res1, 4, "NAND sync", &ret);
|
gpiomtd->io_sync = request_and_remap(res1, 4, "NAND sync", &ret);
|
||||||
if (!gpiomtd->io_sync) {
|
if (!gpiomtd->io_sync) {
|
||||||
|
@ -257,7 +351,9 @@ static int __devinit gpio_nand_probe(struct platform_device *dev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(&gpiomtd->plat, dev->dev.platform_data, sizeof(gpiomtd->plat));
|
ret = gpio_nand_get_config(&dev->dev, &gpiomtd->plat);
|
||||||
|
if (ret)
|
||||||
|
goto err_nce;
|
||||||
|
|
||||||
ret = gpio_request(gpiomtd->plat.gpio_nce, "NAND NCE");
|
ret = gpio_request(gpiomtd->plat.gpio_nce, "NAND NCE");
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -316,8 +412,12 @@ static int __devinit gpio_nand_probe(struct platform_device *dev)
|
||||||
gpiomtd->plat.adjust_parts(&gpiomtd->plat,
|
gpiomtd->plat.adjust_parts(&gpiomtd->plat,
|
||||||
gpiomtd->mtd_info.size);
|
gpiomtd->mtd_info.size);
|
||||||
|
|
||||||
mtd_device_register(&gpiomtd->mtd_info, gpiomtd->plat.parts,
|
ppdata.of_node = dev->dev.of_node;
|
||||||
gpiomtd->plat.num_parts);
|
ret = mtd_device_parse_register(&gpiomtd->mtd_info, NULL, &ppdata,
|
||||||
|
gpiomtd->plat.parts,
|
||||||
|
gpiomtd->plat.num_parts);
|
||||||
|
if (ret)
|
||||||
|
goto err_wp;
|
||||||
platform_set_drvdata(dev, gpiomtd);
|
platform_set_drvdata(dev, gpiomtd);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -352,6 +452,7 @@ static struct platform_driver gpio_nand_driver = {
|
||||||
.remove = gpio_nand_remove,
|
.remove = gpio_nand_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "gpio-nand",
|
.name = "gpio-nand",
|
||||||
|
.of_match_table = gpio_nand_id_table,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue