1
2
3
4
5
6
7
8
9
10
11
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
92
93
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 {
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