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 
25 struct 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 
40 static 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 
55 static 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 
77 static 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 
109 static 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 
140 static const struct of_device_id iproc_nand_of_match[] = {
141 	{ .compatible = "brcm,nand-iproc" },
142 	{},
143 };
144 MODULE_DEVICE_TABLE(of, iproc_nand_of_match);
145 
146 static 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 };
155 module_platform_driver(iproc_nand_driver);
156 
157 MODULE_LICENSE("GPL v2");
158 MODULE_AUTHOR("Brian Norris");
159 MODULE_AUTHOR("Ray Jui");
160 MODULE_DESCRIPTION("NAND driver for Broadcom IPROC-based SoCs");
161