1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright © 2015 Broadcom Corporation
6 #include <linux/device.h>
8 #include <linux/ioport.h>
9 #include <linux/module.h>
11 #include <linux/of_address.h>
12 #include <linux/platform_device.h>
13 #include <linux/slab.h>
17 struct iproc_nand_soc
{
18 struct brcmnand_soc soc
;
20 void __iomem
*idm_base
;
21 void __iomem
*ext_base
;
25 #define IPROC_NAND_CTLR_READY_OFFSET 0x10
26 #define IPROC_NAND_CTLR_READY BIT(0)
28 #define IPROC_NAND_IO_CTRL_OFFSET 0x00
29 #define IPROC_NAND_APB_LE_MODE BIT(24)
30 #define IPROC_NAND_INT_CTRL_READ_ENABLE BIT(6)
32 static bool iproc_nand_intc_ack(struct brcmnand_soc
*soc
)
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
);
39 if (val
& IPROC_NAND_CTLR_READY
) {
40 brcmnand_writel(IPROC_NAND_CTLR_READY
, mmio
);
47 static void iproc_nand_intc_set(struct brcmnand_soc
*soc
, bool en
)
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
;
55 spin_lock_irqsave(&priv
->idm_lock
, flags
);
57 val
= brcmnand_readl(mmio
);
60 val
|= IPROC_NAND_INT_CTRL_READ_ENABLE
;
62 val
&= ~IPROC_NAND_INT_CTRL_READ_ENABLE
;
64 brcmnand_writel(val
, mmio
);
66 spin_unlock_irqrestore(&priv
->idm_lock
, flags
);
69 static void iproc_nand_apb_access(struct brcmnand_soc
*soc
, bool prepare
,
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
;
78 spin_lock_irqsave(&priv
->idm_lock
, flags
);
80 val
= brcmnand_readl(mmio
);
83 * In the case of BE or when dealing with NAND data, alway configure
84 * the APB bus to LE mode before accessing the FIFO and back to BE mode
85 * after the access is done
87 if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN
) || !is_param
) {
89 val
|= IPROC_NAND_APB_LE_MODE
;
91 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
;
96 brcmnand_writel(val
, mmio
);
98 spin_unlock_irqrestore(&priv
->idm_lock
, flags
);
101 static int iproc_nand_probe(struct platform_device
*pdev
)
103 struct device
*dev
= &pdev
->dev
;
104 struct iproc_nand_soc
*priv
;
105 struct brcmnand_soc
*soc
;
106 struct resource
*res
;
108 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
113 spin_lock_init(&priv
->idm_lock
);
115 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
, "iproc-idm");
116 priv
->idm_base
= devm_ioremap_resource(dev
, res
);
117 if (IS_ERR(priv
->idm_base
))
118 return PTR_ERR(priv
->idm_base
);
120 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
, "iproc-ext");
121 priv
->ext_base
= devm_ioremap_resource(dev
, res
);
122 if (IS_ERR(priv
->ext_base
))
123 return PTR_ERR(priv
->ext_base
);
125 soc
->ctlrdy_ack
= iproc_nand_intc_ack
;
126 soc
->ctlrdy_set_enabled
= iproc_nand_intc_set
;
127 soc
->prepare_data_bus
= iproc_nand_apb_access
;
129 return brcmnand_probe(pdev
, soc
);
132 static const struct of_device_id iproc_nand_of_match
[] = {
133 { .compatible
= "brcm,nand-iproc" },
136 MODULE_DEVICE_TABLE(of
, iproc_nand_of_match
);
138 static struct platform_driver iproc_nand_driver
= {
139 .probe
= iproc_nand_probe
,
140 .remove
= brcmnand_remove
,
142 .name
= "iproc_nand",
143 .pm
= &brcmnand_pm_ops
,
144 .of_match_table
= iproc_nand_of_match
,
147 module_platform_driver(iproc_nand_driver
);
149 MODULE_LICENSE("GPL v2");
150 MODULE_AUTHOR("Brian Norris");
151 MODULE_AUTHOR("Ray Jui");
152 MODULE_DESCRIPTION("NAND driver for Broadcom IPROC-based SoCs");