|
@@ -11,6 +11,7 @@
|
|
|
* GNU General Public License for more details.
|
|
|
*/
|
|
|
|
|
|
+#include <linux/clk.h>
|
|
|
#include <linux/version.h>
|
|
|
#include <linux/module.h>
|
|
|
#include <linux/init.h>
|
|
@@ -122,6 +123,9 @@ struct brcmnand_controller {
|
|
|
/* Some SoCs provide custom interrupt status register(s) */
|
|
|
struct brcmnand_soc *soc;
|
|
|
|
|
|
+ /* Some SoCs have a gateable clock for the controller */
|
|
|
+ struct clk *clk;
|
|
|
+
|
|
|
int cmd_pending;
|
|
|
bool dma_pending;
|
|
|
struct completion done;
|
|
@@ -2127,10 +2131,24 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
|
|
|
if (IS_ERR(ctrl->nand_base))
|
|
|
return PTR_ERR(ctrl->nand_base);
|
|
|
|
|
|
+ /* Enable clock before using NAND registers */
|
|
|
+ ctrl->clk = devm_clk_get(dev, "nand");
|
|
|
+ if (!IS_ERR(ctrl->clk)) {
|
|
|
+ ret = clk_prepare_enable(ctrl->clk);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+ } else {
|
|
|
+ ret = PTR_ERR(ctrl->clk);
|
|
|
+ if (ret == -EPROBE_DEFER)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ ctrl->clk = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
/* Initialize NAND revision */
|
|
|
ret = brcmnand_revision_init(ctrl);
|
|
|
if (ret)
|
|
|
- return ret;
|
|
|
+ goto err;
|
|
|
|
|
|
/*
|
|
|
* Most chips have this cache at a fixed offset within 'nand' block.
|
|
@@ -2139,8 +2157,10 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-cache");
|
|
|
if (res) {
|
|
|
ctrl->nand_fc = devm_ioremap_resource(dev, res);
|
|
|
- if (IS_ERR(ctrl->nand_fc))
|
|
|
- return PTR_ERR(ctrl->nand_fc);
|
|
|
+ if (IS_ERR(ctrl->nand_fc)) {
|
|
|
+ ret = PTR_ERR(ctrl->nand_fc);
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
} else {
|
|
|
ctrl->nand_fc = ctrl->nand_base +
|
|
|
ctrl->reg_offsets[BRCMNAND_FC_BASE];
|
|
@@ -2150,8 +2170,10 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "flash-dma");
|
|
|
if (res) {
|
|
|
ctrl->flash_dma_base = devm_ioremap_resource(dev, res);
|
|
|
- if (IS_ERR(ctrl->flash_dma_base))
|
|
|
- return PTR_ERR(ctrl->flash_dma_base);
|
|
|
+ if (IS_ERR(ctrl->flash_dma_base)) {
|
|
|
+ ret = PTR_ERR(ctrl->flash_dma_base);
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
|
|
|
flash_dma_writel(ctrl, FLASH_DMA_MODE, 1); /* linked-list */
|
|
|
flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0);
|
|
@@ -2160,13 +2182,16 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
|
|
|
ctrl->dma_desc = dmam_alloc_coherent(dev,
|
|
|
sizeof(*ctrl->dma_desc),
|
|
|
&ctrl->dma_pa, GFP_KERNEL);
|
|
|
- if (!ctrl->dma_desc)
|
|
|
- return -ENOMEM;
|
|
|
+ if (!ctrl->dma_desc) {
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
|
|
|
ctrl->dma_irq = platform_get_irq(pdev, 1);
|
|
|
if ((int)ctrl->dma_irq < 0) {
|
|
|
dev_err(dev, "missing FLASH_DMA IRQ\n");
|
|
|
- return -ENODEV;
|
|
|
+ ret = -ENODEV;
|
|
|
+ goto err;
|
|
|
}
|
|
|
|
|
|
ret = devm_request_irq(dev, ctrl->dma_irq,
|
|
@@ -2175,7 +2200,7 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
|
|
|
if (ret < 0) {
|
|
|
dev_err(dev, "can't allocate IRQ %d: error %d\n",
|
|
|
ctrl->dma_irq, ret);
|
|
|
- return ret;
|
|
|
+ goto err;
|
|
|
}
|
|
|
|
|
|
dev_info(dev, "enabling FLASH_DMA\n");
|
|
@@ -2199,7 +2224,8 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
|
|
|
ctrl->irq = platform_get_irq(pdev, 0);
|
|
|
if ((int)ctrl->irq < 0) {
|
|
|
dev_err(dev, "no IRQ defined\n");
|
|
|
- return -ENODEV;
|
|
|
+ ret = -ENODEV;
|
|
|
+ goto err;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -2223,7 +2249,7 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
|
|
|
if (ret < 0) {
|
|
|
dev_err(dev, "can't allocate IRQ %d: error %d\n",
|
|
|
ctrl->irq, ret);
|
|
|
- return ret;
|
|
|
+ goto err;
|
|
|
}
|
|
|
|
|
|
for_each_available_child_of_node(dn, child) {
|
|
@@ -2233,7 +2259,8 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
|
|
|
host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
|
|
|
if (!host) {
|
|
|
of_node_put(child);
|
|
|
- return -ENOMEM;
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto err;
|
|
|
}
|
|
|
host->pdev = pdev;
|
|
|
host->ctrl = ctrl;
|
|
@@ -2249,10 +2276,17 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
|
|
|
}
|
|
|
|
|
|
/* No chip-selects could initialize properly */
|
|
|
- if (list_empty(&ctrl->host_list))
|
|
|
- return -ENODEV;
|
|
|
+ if (list_empty(&ctrl->host_list)) {
|
|
|
+ ret = -ENODEV;
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
|
|
|
return 0;
|
|
|
+
|
|
|
+err:
|
|
|
+ clk_disable_unprepare(ctrl->clk);
|
|
|
+ return ret;
|
|
|
+
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(brcmnand_probe);
|
|
|
|
|
@@ -2264,6 +2298,8 @@ int brcmnand_remove(struct platform_device *pdev)
|
|
|
list_for_each_entry(host, &ctrl->host_list, node)
|
|
|
nand_release(&host->mtd);
|
|
|
|
|
|
+ clk_disable_unprepare(ctrl->clk);
|
|
|
+
|
|
|
dev_set_drvdata(&pdev->dev, NULL);
|
|
|
|
|
|
return 0;
|