Merge tag 'nand/for-5.18' into mtd/next
[linux-2.6-microblaze.git] / drivers / mtd / nand / raw / brcmnand / brcmnand.c
index df1b73d..2e9c2e2 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/platform_device.h>
+#include <linux/platform_data/brcmnand.h>
 #include <linux/err.h>
 #include <linux/completion.h>
 #include <linux/interrupt.h>
@@ -25,6 +26,7 @@
 #include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/slab.h>
+#include <linux/static_key.h>
 #include <linux/list.h>
 #include <linux/log2.h>
 
@@ -207,13 +209,15 @@ enum {
 
 struct brcmnand_host;
 
+static DEFINE_STATIC_KEY_FALSE(brcmnand_soc_has_ops_key);
+
 struct brcmnand_controller {
        struct device           *dev;
        struct nand_controller  controller;
        void __iomem            *nand_base;
        void __iomem            *nand_fc; /* flash cache */
        void __iomem            *flash_dma_base;
-       unsigned int            irq;
+       int                     irq;
        unsigned int            dma_irq;
        int                     nand_version;
 
@@ -592,15 +596,29 @@ enum {
        INTFC_CTLR_READY                = BIT(31),
 };
 
+static inline bool brcmnand_non_mmio_ops(struct brcmnand_controller *ctrl)
+{
+#if IS_ENABLED(CONFIG_MTD_NAND_BRCMNAND_BCMA)
+       return static_branch_unlikely(&brcmnand_soc_has_ops_key);
+#else
+       return false;
+#endif
+}
+
 static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs)
 {
+       if (brcmnand_non_mmio_ops(ctrl))
+               return brcmnand_soc_read(ctrl->soc, offs);
        return brcmnand_readl(ctrl->nand_base + offs);
 }
 
 static inline void nand_writereg(struct brcmnand_controller *ctrl, u32 offs,
                                 u32 val)
 {
-       brcmnand_writel(val, ctrl->nand_base + offs);
+       if (brcmnand_non_mmio_ops(ctrl))
+               brcmnand_soc_write(ctrl->soc, val, offs);
+       else
+               brcmnand_writel(val, ctrl->nand_base + offs);
 }
 
 static int brcmnand_revision_init(struct brcmnand_controller *ctrl)
@@ -766,13 +784,18 @@ static inline void brcmnand_rmw_reg(struct brcmnand_controller *ctrl,
 
 static inline u32 brcmnand_read_fc(struct brcmnand_controller *ctrl, int word)
 {
+       if (brcmnand_non_mmio_ops(ctrl))
+               return brcmnand_soc_read(ctrl->soc, BRCMNAND_NON_MMIO_FC_ADDR);
        return __raw_readl(ctrl->nand_fc + word * 4);
 }
 
 static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl,
                                     int word, u32 val)
 {
-       __raw_writel(val, ctrl->nand_fc + word * 4);
+       if (brcmnand_non_mmio_ops(ctrl))
+               brcmnand_soc_write(ctrl->soc, val, BRCMNAND_NON_MMIO_FC_ADDR);
+       else
+               __raw_writel(val, ctrl->nand_fc + word * 4);
 }
 
 static inline void edu_writel(struct brcmnand_controller *ctrl,
@@ -897,6 +920,12 @@ static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val)
 
 static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl)
 {
+       /* Kludge for the BCMA-based NAND controller which does not actually
+        * shift the command
+        */
+       if (ctrl->nand_version == 0x0304 && brcmnand_non_mmio_ops(ctrl))
+               return 0;
+
        if (ctrl->nand_version < 0x0602)
                return 24;
        return 0;
@@ -1592,7 +1621,7 @@ static bool brcmstb_nand_wait_for_completion(struct nand_chip *chip)
        bool err = false;
        int sts;
 
-       if (mtd->oops_panic_write) {
+       if (mtd->oops_panic_write || ctrl->irq < 0) {
                /* switch to interrupt polling and PIO mode */
                disable_ctrl_irqs(ctrl);
                sts = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY,
@@ -2750,33 +2779,27 @@ static const struct nand_controller_ops brcmnand_controller_ops = {
        .attach_chip = brcmnand_attach_chip,
 };
 
-static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
+static int brcmnand_init_cs(struct brcmnand_host *host,
+                           const char * const *part_probe_types)
 {
        struct brcmnand_controller *ctrl = host->ctrl;
-       struct platform_device *pdev = host->pdev;
+       struct device *dev = ctrl->dev;
        struct mtd_info *mtd;
        struct nand_chip *chip;
        int ret;
        u16 cfg_offs;
 
-       ret = of_property_read_u32(dn, "reg", &host->cs);
-       if (ret) {
-               dev_err(&pdev->dev, "can't get chip-select\n");
-               return -ENXIO;
-       }
-
        mtd = nand_to_mtd(&host->chip);
        chip = &host->chip;
 
-       nand_set_flash_node(chip, dn);
        nand_set_controller_data(chip, host);
-       mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "brcmnand.%d",
+       mtd->name = devm_kasprintf(dev, GFP_KERNEL, "brcmnand.%d",
                                   host->cs);
        if (!mtd->name)
                return -ENOMEM;
 
        mtd->owner = THIS_MODULE;
-       mtd->dev.parent = &pdev->dev;
+       mtd->dev.parent = dev;
 
        chip->legacy.cmd_ctrl = brcmnand_cmd_ctrl;
        chip->legacy.cmdfunc = brcmnand_cmdfunc;
@@ -2810,7 +2833,7 @@ static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
        if (ret)
                return ret;
 
-       ret = mtd_device_register(mtd, NULL, 0);
+       ret = mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0);
        if (ret)
                nand_cleanup(chip);
 
@@ -2979,17 +3002,15 @@ static int brcmnand_edu_setup(struct platform_device *pdev)
 
 int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
 {
+       struct brcmnand_platform_data *pd = dev_get_platdata(&pdev->dev);
        struct device *dev = &pdev->dev;
        struct device_node *dn = dev->of_node, *child;
        struct brcmnand_controller *ctrl;
+       struct brcmnand_host *host;
        struct resource *res;
        int ret;
 
-       /* We only support device-tree instantiation */
-       if (!dn)
-               return -ENODEV;
-
-       if (!of_match_node(brcmnand_of_match, dn))
+       if (dn && !of_match_node(brcmnand_of_match, dn))
                return -ENODEV;
 
        ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
@@ -2998,6 +3019,13 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
 
        dev_set_drvdata(dev, ctrl);
        ctrl->dev = dev;
+       ctrl->soc = soc;
+
+       /* Enable the static key if the soc provides I/O operations indicating
+        * that a non-memory mapped IO access path must be used
+        */
+       if (brcmnand_soc_has_ops(ctrl->soc))
+               static_branch_enable(&brcmnand_soc_has_ops_key);
 
        init_completion(&ctrl->done);
        init_completion(&ctrl->dma_done);
@@ -3009,7 +3037,7 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
        /* NAND register range */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        ctrl->nand_base = devm_ioremap_resource(dev, res);
-       if (IS_ERR(ctrl->nand_base))
+       if (IS_ERR(ctrl->nand_base) && !brcmnand_soc_has_ops(soc))
                return PTR_ERR(ctrl->nand_base);
 
        /* Enable clock before using NAND registers */
@@ -3126,40 +3154,33 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
        }
 
        /* IRQ */
-       ctrl->irq = platform_get_irq(pdev, 0);
-       if ((int)ctrl->irq < 0) {
-               dev_err(dev, "no IRQ defined\n");
-               ret = -ENODEV;
-               goto err;
-       }
-
-       /*
-        * Some SoCs integrate this controller (e.g., its interrupt bits) in
-        * interesting ways
-        */
-       if (soc) {
-               ctrl->soc = soc;
-
-               ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0,
-                                      DRV_NAME, ctrl);
+       ctrl->irq = platform_get_irq_optional(pdev, 0);
+       if (ctrl->irq > 0) {
+               /*
+                * Some SoCs integrate this controller (e.g., its interrupt bits) in
+                * interesting ways
+                */
+               if (soc) {
+                       ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0,
+                                              DRV_NAME, ctrl);
 
-               /* Enable interrupt */
-               ctrl->soc->ctlrdy_ack(ctrl->soc);
-               ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
-       } else {
-               /* Use standard interrupt infrastructure */
-               ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0,
-                                      DRV_NAME, ctrl);
-       }
-       if (ret < 0) {
-               dev_err(dev, "can't allocate IRQ %d: error %d\n",
-                       ctrl->irq, ret);
-               goto err;
+                       /* Enable interrupt */
+                       ctrl->soc->ctlrdy_ack(ctrl->soc);
+                       ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
+               } else {
+                       /* Use standard interrupt infrastructure */
+                       ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0,
+                                              DRV_NAME, ctrl);
+               }
+               if (ret < 0) {
+                       dev_err(dev, "can't allocate IRQ %d: error %d\n",
+                               ctrl->irq, ret);
+                       goto err;
+               }
        }
 
        for_each_available_child_of_node(dn, child) {
                if (of_device_is_compatible(child, "brcm,nandcs")) {
-                       struct brcmnand_host *host;
 
                        host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
                        if (!host) {
@@ -3170,7 +3191,16 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
                        host->pdev = pdev;
                        host->ctrl = ctrl;
 
-                       ret = brcmnand_init_cs(host, child);
+                       ret = of_property_read_u32(child, "reg", &host->cs);
+                       if (ret) {
+                               dev_err(dev, "can't get chip-select\n");
+                               devm_kfree(dev, host);
+                               continue;
+                       }
+
+                       nand_set_flash_node(&host->chip, child);
+
+                       ret = brcmnand_init_cs(host, NULL);
                        if (ret) {
                                devm_kfree(dev, host);
                                continue; /* Try all chip-selects */
@@ -3180,6 +3210,32 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
                }
        }
 
+       if (!list_empty(&ctrl->host_list))
+               return 0;
+
+       if (!pd) {
+               ret = -ENODEV;
+               goto err;
+       }
+
+       /* If we got there we must have been probing via platform data */
+       host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
+       if (!host) {
+               ret = -ENOMEM;
+               goto err;
+       }
+       host->pdev = pdev;
+       host->ctrl = ctrl;
+       host->cs = pd->chip_select;
+       host->chip.ecc.size = pd->ecc_stepsize;
+       host->chip.ecc.strength = pd->ecc_strength;
+
+       ret = brcmnand_init_cs(host, pd->part_probe_types);
+       if (ret)
+               goto err;
+
+       list_add_tail(&host->node, &ctrl->host_list);
+
        /* No chip-selects could initialize properly */
        if (list_empty(&ctrl->host_list)) {
                ret = -ENODEV;