diff options
Diffstat (limited to 'drivers/mtd/nand/omap2.c')
-rw-r--r-- | drivers/mtd/nand/omap2.c | 134 |
1 files changed, 114 insertions, 20 deletions
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 7e4e263c7d9c..35b8f3359c17 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <linux/slab.h> | 24 | #include <linux/slab.h> |
25 | #include <linux/of.h> | 25 | #include <linux/of.h> |
26 | #include <linux/of_device.h> | 26 | #include <linux/of_device.h> |
27 | #include <linux/of_mtd.h> | ||
27 | 28 | ||
28 | #include <linux/mtd/nand_bch.h> | 29 | #include <linux/mtd/nand_bch.h> |
29 | #include <linux/platform_data/elm.h> | 30 | #include <linux/platform_data/elm.h> |
@@ -176,11 +177,11 @@ struct omap_nand_info { | |||
176 | /* Interface to GPMC */ | 177 | /* Interface to GPMC */ |
177 | struct gpmc_nand_regs reg; | 178 | struct gpmc_nand_regs reg; |
178 | struct gpmc_nand_ops *ops; | 179 | struct gpmc_nand_ops *ops; |
180 | bool flash_bbt; | ||
179 | /* generated at runtime depending on ECC algorithm and layout selected */ | 181 | /* generated at runtime depending on ECC algorithm and layout selected */ |
180 | struct nand_ecclayout oobinfo; | 182 | struct nand_ecclayout oobinfo; |
181 | /* fields specific for BCHx_HW ECC scheme */ | 183 | /* fields specific for BCHx_HW ECC scheme */ |
182 | struct device *elm_dev; | 184 | struct device *elm_dev; |
183 | struct device_node *of_node; | ||
184 | }; | 185 | }; |
185 | 186 | ||
186 | static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd) | 187 | static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd) |
@@ -1643,10 +1644,86 @@ static bool omap2_nand_ecc_check(struct omap_nand_info *info, | |||
1643 | return true; | 1644 | return true; |
1644 | } | 1645 | } |
1645 | 1646 | ||
1647 | static const char * const nand_xfer_types[] = { | ||
1648 | [NAND_OMAP_PREFETCH_POLLED] = "prefetch-polled", | ||
1649 | [NAND_OMAP_POLLED] = "polled", | ||
1650 | [NAND_OMAP_PREFETCH_DMA] = "prefetch-dma", | ||
1651 | [NAND_OMAP_PREFETCH_IRQ] = "prefetch-irq", | ||
1652 | }; | ||
1653 | |||
1654 | static int omap_get_dt_info(struct device *dev, struct omap_nand_info *info) | ||
1655 | { | ||
1656 | struct device_node *child = dev->of_node; | ||
1657 | int i; | ||
1658 | const char *s; | ||
1659 | u32 cs; | ||
1660 | |||
1661 | if (of_property_read_u32(child, "reg", &cs) < 0) { | ||
1662 | dev_err(dev, "reg not found in DT\n"); | ||
1663 | return -EINVAL; | ||
1664 | } | ||
1665 | |||
1666 | info->gpmc_cs = cs; | ||
1667 | |||
1668 | /* detect availability of ELM module. Won't be present pre-OMAP4 */ | ||
1669 | info->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0); | ||
1670 | if (!info->elm_of_node) | ||
1671 | dev_dbg(dev, "ti,elm-id not in DT\n"); | ||
1672 | |||
1673 | /* select ecc-scheme for NAND */ | ||
1674 | if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) { | ||
1675 | dev_err(dev, "ti,nand-ecc-opt not found\n"); | ||
1676 | return -EINVAL; | ||
1677 | } | ||
1678 | |||
1679 | if (!strcmp(s, "sw")) { | ||
1680 | info->ecc_opt = OMAP_ECC_HAM1_CODE_SW; | ||
1681 | } else if (!strcmp(s, "ham1") || | ||
1682 | !strcmp(s, "hw") || !strcmp(s, "hw-romcode")) { | ||
1683 | info->ecc_opt = OMAP_ECC_HAM1_CODE_HW; | ||
1684 | } else if (!strcmp(s, "bch4")) { | ||
1685 | if (info->elm_of_node) | ||
1686 | info->ecc_opt = OMAP_ECC_BCH4_CODE_HW; | ||
1687 | else | ||
1688 | info->ecc_opt = OMAP_ECC_BCH4_CODE_HW_DETECTION_SW; | ||
1689 | } else if (!strcmp(s, "bch8")) { | ||
1690 | if (info->elm_of_node) | ||
1691 | info->ecc_opt = OMAP_ECC_BCH8_CODE_HW; | ||
1692 | else | ||
1693 | info->ecc_opt = OMAP_ECC_BCH8_CODE_HW_DETECTION_SW; | ||
1694 | } else if (!strcmp(s, "bch16")) { | ||
1695 | info->ecc_opt = OMAP_ECC_BCH16_CODE_HW; | ||
1696 | } else { | ||
1697 | dev_err(dev, "unrecognized value for ti,nand-ecc-opt\n"); | ||
1698 | return -EINVAL; | ||
1699 | } | ||
1700 | |||
1701 | /* select data transfer mode */ | ||
1702 | if (!of_property_read_string(child, "ti,nand-xfer-type", &s)) { | ||
1703 | for (i = 0; i < ARRAY_SIZE(nand_xfer_types); i++) { | ||
1704 | if (!strcasecmp(s, nand_xfer_types[i])) { | ||
1705 | info->xfer_type = i; | ||
1706 | goto next; | ||
1707 | } | ||
1708 | } | ||
1709 | |||
1710 | dev_err(dev, "unrecognized value for ti,nand-xfer-type\n"); | ||
1711 | return -EINVAL; | ||
1712 | } | ||
1713 | |||
1714 | next: | ||
1715 | of_get_nand_on_flash_bbt(child); | ||
1716 | |||
1717 | if (of_get_nand_bus_width(child) == 16) | ||
1718 | info->devsize = NAND_BUSWIDTH_16; | ||
1719 | |||
1720 | return 0; | ||
1721 | } | ||
1722 | |||
1646 | static int omap_nand_probe(struct platform_device *pdev) | 1723 | static int omap_nand_probe(struct platform_device *pdev) |
1647 | { | 1724 | { |
1648 | struct omap_nand_info *info; | 1725 | struct omap_nand_info *info; |
1649 | struct omap_nand_platform_data *pdata; | 1726 | struct omap_nand_platform_data *pdata = NULL; |
1650 | struct mtd_info *mtd; | 1727 | struct mtd_info *mtd; |
1651 | struct nand_chip *nand_chip; | 1728 | struct nand_chip *nand_chip; |
1652 | struct nand_ecclayout *ecclayout; | 1729 | struct nand_ecclayout *ecclayout; |
@@ -1656,39 +1733,47 @@ static int omap_nand_probe(struct platform_device *pdev) | |||
1656 | unsigned sig; | 1733 | unsigned sig; |
1657 | unsigned oob_index; | 1734 | unsigned oob_index; |
1658 | struct resource *res; | 1735 | struct resource *res; |
1659 | 1736 | struct device *dev = &pdev->dev; | |
1660 | pdata = dev_get_platdata(&pdev->dev); | ||
1661 | if (pdata == NULL) { | ||
1662 | dev_err(&pdev->dev, "platform data missing\n"); | ||
1663 | return -ENODEV; | ||
1664 | } | ||
1665 | 1737 | ||
1666 | info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info), | 1738 | info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info), |
1667 | GFP_KERNEL); | 1739 | GFP_KERNEL); |
1668 | if (!info) | 1740 | if (!info) |
1669 | return -ENOMEM; | 1741 | return -ENOMEM; |
1670 | 1742 | ||
1671 | platform_set_drvdata(pdev, info); | 1743 | info->pdev = pdev; |
1672 | 1744 | ||
1745 | if (dev->of_node) { | ||
1746 | if (omap_get_dt_info(dev, info)) | ||
1747 | return -EINVAL; | ||
1748 | } else { | ||
1749 | pdata = dev_get_platdata(&pdev->dev); | ||
1750 | if (!pdata) { | ||
1751 | dev_err(&pdev->dev, "platform data missing\n"); | ||
1752 | return -EINVAL; | ||
1753 | } | ||
1754 | |||
1755 | info->gpmc_cs = pdata->cs; | ||
1756 | info->reg = pdata->reg; | ||
1757 | info->ecc_opt = pdata->ecc_opt; | ||
1758 | info->dev_ready = pdata->dev_ready; | ||
1759 | info->xfer_type = pdata->xfer_type; | ||
1760 | info->devsize = pdata->devsize; | ||
1761 | info->elm_of_node = pdata->elm_of_node; | ||
1762 | info->flash_bbt = pdata->flash_bbt; | ||
1763 | } | ||
1764 | |||
1765 | platform_set_drvdata(pdev, info); | ||
1673 | info->ops = gpmc_omap_get_nand_ops(&info->reg, info->gpmc_cs); | 1766 | info->ops = gpmc_omap_get_nand_ops(&info->reg, info->gpmc_cs); |
1674 | if (!info->ops) { | 1767 | if (!info->ops) { |
1675 | dev_err(&pdev->dev, "Failed to get GPMC->NAND interface\n"); | 1768 | dev_err(&pdev->dev, "Failed to get GPMC->NAND interface\n"); |
1676 | return -ENODEV; | 1769 | return -ENODEV; |
1677 | } | 1770 | } |
1678 | info->pdev = pdev; | ||
1679 | info->gpmc_cs = pdata->cs; | ||
1680 | info->of_node = pdata->of_node; | ||
1681 | info->ecc_opt = pdata->ecc_opt; | ||
1682 | info->dev_ready = pdata->dev_ready; | ||
1683 | info->xfer_type = pdata->xfer_type; | ||
1684 | info->devsize = pdata->devsize; | ||
1685 | info->elm_of_node = pdata->elm_of_node; | ||
1686 | 1771 | ||
1687 | nand_chip = &info->nand; | 1772 | nand_chip = &info->nand; |
1688 | mtd = nand_to_mtd(nand_chip); | 1773 | mtd = nand_to_mtd(nand_chip); |
1689 | mtd->dev.parent = &pdev->dev; | 1774 | mtd->dev.parent = &pdev->dev; |
1690 | nand_chip->ecc.priv = NULL; | 1775 | nand_chip->ecc.priv = NULL; |
1691 | nand_set_flash_node(nand_chip, pdata->of_node); | 1776 | nand_set_flash_node(nand_chip, dev->of_node); |
1692 | 1777 | ||
1693 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1778 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
1694 | nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res); | 1779 | nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res); |
@@ -1717,7 +1802,7 @@ static int omap_nand_probe(struct platform_device *pdev) | |||
1717 | nand_chip->chip_delay = 50; | 1802 | nand_chip->chip_delay = 50; |
1718 | } | 1803 | } |
1719 | 1804 | ||
1720 | if (pdata->flash_bbt) | 1805 | if (info->flash_bbt) |
1721 | nand_chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; | 1806 | nand_chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; |
1722 | else | 1807 | else |
1723 | nand_chip->options |= NAND_SKIP_BBTSCAN; | 1808 | nand_chip->options |= NAND_SKIP_BBTSCAN; |
@@ -2035,7 +2120,10 @@ scan_tail: | |||
2035 | goto return_error; | 2120 | goto return_error; |
2036 | } | 2121 | } |
2037 | 2122 | ||
2038 | mtd_device_register(mtd, pdata->parts, pdata->nr_parts); | 2123 | if (dev->of_node) |
2124 | mtd_device_register(mtd, NULL, 0); | ||
2125 | else | ||
2126 | mtd_device_register(mtd, pdata->parts, pdata->nr_parts); | ||
2039 | 2127 | ||
2040 | platform_set_drvdata(pdev, mtd); | 2128 | platform_set_drvdata(pdev, mtd); |
2041 | 2129 | ||
@@ -2066,11 +2154,17 @@ static int omap_nand_remove(struct platform_device *pdev) | |||
2066 | return 0; | 2154 | return 0; |
2067 | } | 2155 | } |
2068 | 2156 | ||
2157 | static const struct of_device_id omap_nand_ids[] = { | ||
2158 | { .compatible = "ti,omap2-nand", }, | ||
2159 | {}, | ||
2160 | }; | ||
2161 | |||
2069 | static struct platform_driver omap_nand_driver = { | 2162 | static struct platform_driver omap_nand_driver = { |
2070 | .probe = omap_nand_probe, | 2163 | .probe = omap_nand_probe, |
2071 | .remove = omap_nand_remove, | 2164 | .remove = omap_nand_remove, |
2072 | .driver = { | 2165 | .driver = { |
2073 | .name = DRIVER_NAME, | 2166 | .name = DRIVER_NAME, |
2167 | .of_match_table = of_match_ptr(omap_nand_ids), | ||
2074 | }, | 2168 | }, |
2075 | }; | 2169 | }; |
2076 | 2170 | ||