1 // SPDX-License-Identifier: GPL-2.0-or-later
4 * Platform independent driver for NDFC (NanD Flash Controller)
5 * integrated into EP440 cores
7 * Ported to an OF platform driver by Sean MacLennan
9 * The NDFC supports multiple chips, but this driver only supports a
10 * single chip since I do not have access to any boards with
13 * Author: Thomas Gleixner
16 * Copyright 2008 PIKA Technologies
17 * Sean MacLennan <smaclennan@pikatech.com>
19 #include <linux/module.h>
20 #include <linux/mtd/rawnand.h>
21 #include <linux/mtd/partitions.h>
22 #include <linux/mtd/ndfc.h>
23 #include <linux/slab.h>
24 #include <linux/mtd/mtd.h>
25 #include <linux/of_address.h>
26 #include <linux/of_platform.h>
31 struct ndfc_controller
{
32 struct platform_device
*ofdev
;
33 void __iomem
*ndfcbase
;
34 struct nand_chip chip
;
36 struct nand_controller ndfc_control
;
39 static struct ndfc_controller ndfc_ctrl
[NDFC_MAX_CS
];
41 static void ndfc_select_chip(struct nand_chip
*nchip
, int chip
)
44 struct ndfc_controller
*ndfc
= nand_get_controller_data(nchip
);
46 ccr
= in_be32(ndfc
->ndfcbase
+ NDFC_CCR
);
48 ccr
&= ~NDFC_CCR_BS_MASK
;
49 ccr
|= NDFC_CCR_BS(chip
+ ndfc
->chip_select
);
51 ccr
|= NDFC_CCR_RESET_CE
;
52 out_be32(ndfc
->ndfcbase
+ NDFC_CCR
, ccr
);
55 static void ndfc_hwcontrol(struct nand_chip
*chip
, int cmd
, unsigned int ctrl
)
57 struct ndfc_controller
*ndfc
= nand_get_controller_data(chip
);
59 if (cmd
== NAND_CMD_NONE
)
63 writel(cmd
& 0xFF, ndfc
->ndfcbase
+ NDFC_CMD
);
65 writel(cmd
& 0xFF, ndfc
->ndfcbase
+ NDFC_ALE
);
68 static int ndfc_ready(struct nand_chip
*chip
)
70 struct ndfc_controller
*ndfc
= nand_get_controller_data(chip
);
72 return in_be32(ndfc
->ndfcbase
+ NDFC_STAT
) & NDFC_STAT_IS_READY
;
75 static void ndfc_enable_hwecc(struct nand_chip
*chip
, int mode
)
78 struct ndfc_controller
*ndfc
= nand_get_controller_data(chip
);
80 ccr
= in_be32(ndfc
->ndfcbase
+ NDFC_CCR
);
81 ccr
|= NDFC_CCR_RESET_ECC
;
82 out_be32(ndfc
->ndfcbase
+ NDFC_CCR
, ccr
);
86 static int ndfc_calculate_ecc(struct nand_chip
*chip
,
87 const u_char
*dat
, u_char
*ecc_code
)
89 struct ndfc_controller
*ndfc
= nand_get_controller_data(chip
);
91 uint8_t *p
= (uint8_t *)&ecc
;
94 ecc
= in_be32(ndfc
->ndfcbase
+ NDFC_ECC
);
95 /* The NDFC uses Smart Media (SMC) bytes order */
104 * Speedups for buffer read/write/verify
106 * NDFC allows 32bit read/write of data. So we can speed up the buffer
107 * functions. No further checking, as nand_base will always read/write
110 static void ndfc_read_buf(struct nand_chip
*chip
, uint8_t *buf
, int len
)
112 struct ndfc_controller
*ndfc
= nand_get_controller_data(chip
);
113 uint32_t *p
= (uint32_t *) buf
;
115 for(;len
> 0; len
-= 4)
116 *p
++ = in_be32(ndfc
->ndfcbase
+ NDFC_DATA
);
119 static void ndfc_write_buf(struct nand_chip
*chip
, const uint8_t *buf
, int len
)
121 struct ndfc_controller
*ndfc
= nand_get_controller_data(chip
);
122 uint32_t *p
= (uint32_t *) buf
;
124 for(;len
> 0; len
-= 4)
125 out_be32(ndfc
->ndfcbase
+ NDFC_DATA
, *p
++);
129 * Initialize chip structure
131 static int ndfc_chip_init(struct ndfc_controller
*ndfc
,
132 struct device_node
*node
)
134 struct device_node
*flash_np
;
135 struct nand_chip
*chip
= &ndfc
->chip
;
136 struct mtd_info
*mtd
= nand_to_mtd(chip
);
139 chip
->legacy
.IO_ADDR_R
= ndfc
->ndfcbase
+ NDFC_DATA
;
140 chip
->legacy
.IO_ADDR_W
= ndfc
->ndfcbase
+ NDFC_DATA
;
141 chip
->legacy
.cmd_ctrl
= ndfc_hwcontrol
;
142 chip
->legacy
.dev_ready
= ndfc_ready
;
143 chip
->legacy
.select_chip
= ndfc_select_chip
;
144 chip
->legacy
.chip_delay
= 50;
145 chip
->controller
= &ndfc
->ndfc_control
;
146 chip
->legacy
.read_buf
= ndfc_read_buf
;
147 chip
->legacy
.write_buf
= ndfc_write_buf
;
148 chip
->ecc
.correct
= rawnand_sw_hamming_correct
;
149 chip
->ecc
.hwctl
= ndfc_enable_hwecc
;
150 chip
->ecc
.calculate
= ndfc_calculate_ecc
;
151 chip
->ecc
.engine_type
= NAND_ECC_ENGINE_TYPE_ON_HOST
;
152 chip
->ecc
.size
= 256;
154 chip
->ecc
.strength
= 1;
155 nand_set_controller_data(chip
, ndfc
);
157 mtd
->dev
.parent
= &ndfc
->ofdev
->dev
;
159 flash_np
= of_get_next_child(node
, NULL
);
162 nand_set_flash_node(chip
, flash_np
);
164 mtd
->name
= kasprintf(GFP_KERNEL
, "%s.%pOFn", dev_name(&ndfc
->ofdev
->dev
),
171 ret
= nand_scan(chip
, 1);
175 ret
= mtd_device_register(mtd
, NULL
, 0);
178 of_node_put(flash_np
);
184 static int ndfc_probe(struct platform_device
*ofdev
)
186 struct ndfc_controller
*ndfc
;
192 /* Read the reg property to get the chip select */
193 reg
= of_get_property(ofdev
->dev
.of_node
, "reg", &len
);
194 if (reg
== NULL
|| len
!= 12) {
195 dev_err(&ofdev
->dev
, "unable read reg property (%d)\n", len
);
199 cs
= be32_to_cpu(reg
[0]);
200 if (cs
>= NDFC_MAX_CS
) {
201 dev_err(&ofdev
->dev
, "invalid CS number (%d)\n", cs
);
205 ndfc
= &ndfc_ctrl
[cs
];
206 ndfc
->chip_select
= cs
;
208 nand_controller_init(&ndfc
->ndfc_control
);
210 dev_set_drvdata(&ofdev
->dev
, ndfc
);
212 ndfc
->ndfcbase
= of_iomap(ofdev
->dev
.of_node
, 0);
213 if (!ndfc
->ndfcbase
) {
214 dev_err(&ofdev
->dev
, "failed to get memory\n");
218 ccr
= NDFC_CCR_BS(ndfc
->chip_select
);
220 /* It is ok if ccr does not exist - just default to 0 */
221 reg
= of_get_property(ofdev
->dev
.of_node
, "ccr", NULL
);
223 ccr
|= be32_to_cpup(reg
);
225 out_be32(ndfc
->ndfcbase
+ NDFC_CCR
, ccr
);
227 /* Set the bank settings if given */
228 reg
= of_get_property(ofdev
->dev
.of_node
, "bank-settings", NULL
);
230 int offset
= NDFC_BCFG0
+ (ndfc
->chip_select
<< 2);
231 out_be32(ndfc
->ndfcbase
+ offset
, be32_to_cpup(reg
));
234 err
= ndfc_chip_init(ndfc
, ofdev
->dev
.of_node
);
236 iounmap(ndfc
->ndfcbase
);
243 static int ndfc_remove(struct platform_device
*ofdev
)
245 struct ndfc_controller
*ndfc
= dev_get_drvdata(&ofdev
->dev
);
246 struct nand_chip
*chip
= &ndfc
->chip
;
247 struct mtd_info
*mtd
= nand_to_mtd(chip
);
250 ret
= mtd_device_unregister(mtd
);
258 static const struct of_device_id ndfc_match
[] = {
259 { .compatible
= "ibm,ndfc", },
262 MODULE_DEVICE_TABLE(of
, ndfc_match
);
264 static struct platform_driver ndfc_driver
= {
267 .of_match_table
= ndfc_match
,
270 .remove
= ndfc_remove
,
273 module_platform_driver(ndfc_driver
);
275 MODULE_LICENSE("GPL");
276 MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
277 MODULE_DESCRIPTION("OF Platform driver for NDFC");