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