linux/drivers/mtd/nand/raw/brcmnand/iproc_nand.c
<<
>>
Prefs
   1/*
   2 * Copyright © 2015 Broadcom Corporation
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 *
   8 * This program is distributed in the hope that it will be useful,
   9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11 * GNU General Public License for more details.
  12 */
  13
  14#include <linux/device.h>
  15#include <linux/io.h>
  16#include <linux/ioport.h>
  17#include <linux/module.h>
  18#include <linux/of.h>
  19#include <linux/of_address.h>
  20#include <linux/platform_device.h>
  21#include <linux/slab.h>
  22
  23#include "brcmnand.h"
  24
  25struct iproc_nand_soc {
  26        struct brcmnand_soc soc;
  27
  28        void __iomem *idm_base;
  29        void __iomem *ext_base;
  30        spinlock_t idm_lock;
  31};
  32
  33#define IPROC_NAND_CTLR_READY_OFFSET       0x10
  34#define IPROC_NAND_CTLR_READY              BIT(0)
  35
  36#define IPROC_NAND_IO_CTRL_OFFSET          0x00
  37#define IPROC_NAND_APB_LE_MODE             BIT(24)
  38#define IPROC_NAND_INT_CTRL_READ_ENABLE    BIT(6)
  39
  40static bool iproc_nand_intc_ack(struct brcmnand_soc *soc)
  41{
  42        struct iproc_nand_soc *priv =
  43                        container_of(soc, struct iproc_nand_soc, soc);
  44        void __iomem *mmio = priv->ext_base + IPROC_NAND_CTLR_READY_OFFSET;
  45        u32 val = brcmnand_readl(mmio);
  46
  47        if (val & IPROC_NAND_CTLR_READY) {
  48                brcmnand_writel(IPROC_NAND_CTLR_READY, mmio);
  49                return true;
  50        }
  51
  52        return false;
  53}
  54
  55static void iproc_nand_intc_set(struct brcmnand_soc *soc, bool en)
  56{
  57        struct iproc_nand_soc *priv =
  58                        container_of(soc, struct iproc_nand_soc, soc);
  59        void __iomem *mmio = priv->idm_base + IPROC_NAND_IO_CTRL_OFFSET;
  60        u32 val;
  61        unsigned long flags;
  62
  63        spin_lock_irqsave(&priv->idm_lock, flags);
  64
  65        val = brcmnand_readl(mmio);
  66
  67        if (en)
  68                val |= IPROC_NAND_INT_CTRL_READ_ENABLE;
  69        else
  70                val &= ~IPROC_NAND_INT_CTRL_READ_ENABLE;
  71
  72        brcmnand_writel(val, mmio);
  73
  74        spin_unlock_irqrestore(&priv->idm_lock, flags);
  75}
  76
  77static void iproc_nand_apb_access(struct brcmnand_soc *soc, bool prepare,
  78                                  bool is_param)
  79{
  80        struct iproc_nand_soc *priv =
  81                        container_of(soc, struct iproc_nand_soc, soc);
  82        void __iomem *mmio = priv->idm_base + IPROC_NAND_IO_CTRL_OFFSET;
  83        u32 val;
  84        unsigned long flags;
  85
  86        spin_lock_irqsave(&priv->idm_lock, flags);
  87
  88        val = brcmnand_readl(mmio);
  89
  90        /*
  91         * In the case of BE or when dealing with NAND data, alway configure
  92         * the APB bus to LE mode before accessing the FIFO and back to BE mode
  93         * after the access is done
  94         */
  95        if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) || !is_param) {
  96                if (prepare)
  97                        val |= IPROC_NAND_APB_LE_MODE;
  98                else
  99                        val &= ~IPROC_NAND_APB_LE_MODE;
 100        } else { /* when in LE accessing the parameter page, keep APB in BE */
 101                val &= ~IPROC_NAND_APB_LE_MODE;
 102        }
 103
 104        brcmnand_writel(val, mmio);
 105
 106        spin_unlock_irqrestore(&priv->idm_lock, flags);
 107}
 108
 109static int iproc_nand_probe(struct platform_device *pdev)
 110{
 111        struct device *dev = &pdev->dev;
 112        struct iproc_nand_soc *priv;
 113        struct brcmnand_soc *soc;
 114        struct resource *res;
 115
 116        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 117        if (!priv)
 118                return -ENOMEM;
 119        soc = &priv->soc;
 120
 121        spin_lock_init(&priv->idm_lock);
 122
 123        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iproc-idm");
 124        priv->idm_base = devm_ioremap_resource(dev, res);
 125        if (IS_ERR(priv->idm_base))
 126                return PTR_ERR(priv->idm_base);
 127
 128        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iproc-ext");
 129        priv->ext_base = devm_ioremap_resource(dev, res);
 130        if (IS_ERR(priv->ext_base))
 131                return PTR_ERR(priv->ext_base);
 132
 133        soc->ctlrdy_ack = iproc_nand_intc_ack;
 134        soc->ctlrdy_set_enabled = iproc_nand_intc_set;
 135        soc->prepare_data_bus = iproc_nand_apb_access;
 136
 137        return brcmnand_probe(pdev, soc);
 138}
 139
 140static const struct of_device_id iproc_nand_of_match[] = {
 141        { .compatible = "brcm,nand-iproc" },
 142        {},
 143};
 144MODULE_DEVICE_TABLE(of, iproc_nand_of_match);
 145
 146static struct platform_driver iproc_nand_driver = {
 147        .probe                  = iproc_nand_probe,
 148        .remove                 = brcmnand_remove,
 149        .driver = {
 150                .name           = "iproc_nand",
 151                .pm             = &brcmnand_pm_ops,
 152                .of_match_table = iproc_nand_of_match,
 153        }
 154};
 155module_platform_driver(iproc_nand_driver);
 156
 157MODULE_LICENSE("GPL v2");
 158MODULE_AUTHOR("Brian Norris");
 159MODULE_AUTHOR("Ray Jui");
 160MODULE_DESCRIPTION("NAND driver for Broadcom IPROC-based SoCs");
 161