150 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright © 2015 Broadcom Corporation4 */5 6#include <linux/device.h>7#include <linux/io.h>8#include <linux/ioport.h>9#include <linux/module.h>10#include <linux/of.h>11#include <linux/of_address.h>12#include <linux/platform_device.h>13#include <linux/slab.h>14 15#include "brcmnand.h"16 17struct iproc_nand_soc {18 struct brcmnand_soc soc;19 20 void __iomem *idm_base;21 void __iomem *ext_base;22 spinlock_t idm_lock;23};24 25#define IPROC_NAND_CTLR_READY_OFFSET 0x1026#define IPROC_NAND_CTLR_READY BIT(0)27 28#define IPROC_NAND_IO_CTRL_OFFSET 0x0029#define IPROC_NAND_APB_LE_MODE BIT(24)30#define IPROC_NAND_INT_CTRL_READ_ENABLE BIT(6)31 32static bool iproc_nand_intc_ack(struct brcmnand_soc *soc)33{34 struct iproc_nand_soc *priv =35 container_of(soc, struct iproc_nand_soc, soc);36 void __iomem *mmio = priv->ext_base + IPROC_NAND_CTLR_READY_OFFSET;37 u32 val = brcmnand_readl(mmio);38 39 if (val & IPROC_NAND_CTLR_READY) {40 brcmnand_writel(IPROC_NAND_CTLR_READY, mmio);41 return true;42 }43 44 return false;45}46 47static void iproc_nand_intc_set(struct brcmnand_soc *soc, bool en)48{49 struct iproc_nand_soc *priv =50 container_of(soc, struct iproc_nand_soc, soc);51 void __iomem *mmio = priv->idm_base + IPROC_NAND_IO_CTRL_OFFSET;52 u32 val;53 unsigned long flags;54 55 spin_lock_irqsave(&priv->idm_lock, flags);56 57 val = brcmnand_readl(mmio);58 59 if (en)60 val |= IPROC_NAND_INT_CTRL_READ_ENABLE;61 else62 val &= ~IPROC_NAND_INT_CTRL_READ_ENABLE;63 64 brcmnand_writel(val, mmio);65 66 spin_unlock_irqrestore(&priv->idm_lock, flags);67}68 69static void iproc_nand_apb_access(struct brcmnand_soc *soc, bool prepare,70 bool is_param)71{72 struct iproc_nand_soc *priv =73 container_of(soc, struct iproc_nand_soc, soc);74 void __iomem *mmio = priv->idm_base + IPROC_NAND_IO_CTRL_OFFSET;75 u32 val;76 unsigned long flags;77 78 spin_lock_irqsave(&priv->idm_lock, flags);79 80 val = brcmnand_readl(mmio);81 82 /*83 * In the case of BE or when dealing with NAND data, alway configure84 * the APB bus to LE mode before accessing the FIFO and back to BE mode85 * after the access is done86 */87 if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) || !is_param) {88 if (prepare)89 val |= IPROC_NAND_APB_LE_MODE;90 else91 val &= ~IPROC_NAND_APB_LE_MODE;92 } else { /* when in LE accessing the parameter page, keep APB in BE */93 val &= ~IPROC_NAND_APB_LE_MODE;94 }95 96 brcmnand_writel(val, mmio);97 98 spin_unlock_irqrestore(&priv->idm_lock, flags);99}100 101static int iproc_nand_probe(struct platform_device *pdev)102{103 struct device *dev = &pdev->dev;104 struct iproc_nand_soc *priv;105 struct brcmnand_soc *soc;106 107 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);108 if (!priv)109 return -ENOMEM;110 soc = &priv->soc;111 112 spin_lock_init(&priv->idm_lock);113 114 priv->idm_base = devm_platform_ioremap_resource_byname(pdev, "iproc-idm");115 if (IS_ERR(priv->idm_base))116 return PTR_ERR(priv->idm_base);117 118 priv->ext_base = devm_platform_ioremap_resource_byname(pdev, "iproc-ext");119 if (IS_ERR(priv->ext_base))120 return PTR_ERR(priv->ext_base);121 122 soc->ctlrdy_ack = iproc_nand_intc_ack;123 soc->ctlrdy_set_enabled = iproc_nand_intc_set;124 soc->prepare_data_bus = iproc_nand_apb_access;125 126 return brcmnand_probe(pdev, soc);127}128 129static const struct of_device_id iproc_nand_of_match[] = {130 { .compatible = "brcm,nand-iproc" },131 {},132};133MODULE_DEVICE_TABLE(of, iproc_nand_of_match);134 135static struct platform_driver iproc_nand_driver = {136 .probe = iproc_nand_probe,137 .remove_new = brcmnand_remove,138 .driver = {139 .name = "iproc_nand",140 .pm = &brcmnand_pm_ops,141 .of_match_table = iproc_nand_of_match,142 }143};144module_platform_driver(iproc_nand_driver);145 146MODULE_LICENSE("GPL v2");147MODULE_AUTHOR("Brian Norris");148MODULE_AUTHOR("Ray Jui");149MODULE_DESCRIPTION("NAND driver for Broadcom IPROC-based SoCs");150