diff options
author | Sean MacLennan <smaclennan@pikatech.com> | 2008-12-10 08:16:34 -0500 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2009-01-05 11:42:44 -0500 |
commit | a808ad3b0d28411e2838117c5b2ae680ae42483c (patch) | |
tree | edd291f3873ca95e0fb2a434034d30669666b6f4 | |
parent | 647b0d3854d776485faea9de7e7ed7fd4bdc6f20 (diff) |
[MTD] [NAND] ndfc driver
The current ndfc driver only compiles under arch/ppc. This arch was
removed from the kernel. I notice the event entry for the ndfc in
Kconfig has been removed in 2.6.28.
This patch converts the ndfc to a proper OF (OpenFirmware) driver. I
can give a working example of the DTS if needed.
The patch has been in production use on the PIKA Warp Appliance and is
in use by others. The Warp basically boots from NAND, so the ndfc driver
is very important to us.
Signed-off-by: Sean MacLennan <smaclennan@pikatech.com>
Acked-By: Josh Boyer <jwboyer@linux.vnet.ibm.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
-rw-r--r-- | Documentation/powerpc/dts-bindings/4xx/ndfc.txt | 39 | ||||
-rw-r--r-- | drivers/mtd/nand/Kconfig | 7 | ||||
-rw-r--r-- | drivers/mtd/nand/ndfc.c | 269 |
3 files changed, 179 insertions, 136 deletions
diff --git a/Documentation/powerpc/dts-bindings/4xx/ndfc.txt b/Documentation/powerpc/dts-bindings/4xx/ndfc.txt new file mode 100644 index 000000000000..869f0b5f16e8 --- /dev/null +++ b/Documentation/powerpc/dts-bindings/4xx/ndfc.txt | |||
@@ -0,0 +1,39 @@ | |||
1 | AMCC NDFC (NanD Flash Controller) | ||
2 | |||
3 | Required properties: | ||
4 | - compatible : "ibm,ndfc". | ||
5 | - reg : should specify chip select and size used for the chip (0x2000). | ||
6 | |||
7 | Optional properties: | ||
8 | - ccr : NDFC config and control register value (default 0). | ||
9 | - bank-settings : NDFC bank configuration register value (default 0). | ||
10 | |||
11 | Notes: | ||
12 | - partition(s) - follows the OF MTD standard for partitions | ||
13 | |||
14 | Example: | ||
15 | |||
16 | ndfc@1,0 { | ||
17 | compatible = "ibm,ndfc"; | ||
18 | reg = <0x00000001 0x00000000 0x00002000>; | ||
19 | ccr = <0x00001000>; | ||
20 | bank-settings = <0x80002222>; | ||
21 | #address-cells = <1>; | ||
22 | #size-cells = <1>; | ||
23 | |||
24 | nand { | ||
25 | #address-cells = <1>; | ||
26 | #size-cells = <1>; | ||
27 | |||
28 | partition@0 { | ||
29 | label = "kernel"; | ||
30 | reg = <0x00000000 0x00200000>; | ||
31 | }; | ||
32 | partition@200000 { | ||
33 | label = "root"; | ||
34 | reg = <0x00200000 0x03E00000>; | ||
35 | }; | ||
36 | }; | ||
37 | }; | ||
38 | |||
39 | |||
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index f8ae0400c49c..8b12e6e109d3 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig | |||
@@ -163,6 +163,13 @@ config MTD_NAND_S3C2410_HWECC | |||
163 | incorrect ECC generation, and if using these, the default of | 163 | incorrect ECC generation, and if using these, the default of |
164 | software ECC is preferable. | 164 | software ECC is preferable. |
165 | 165 | ||
166 | config MTD_NAND_NDFC | ||
167 | tristate "NDFC NanD Flash Controller" | ||
168 | depends on 4xx | ||
169 | select MTD_NAND_ECC_SMC | ||
170 | help | ||
171 | NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs | ||
172 | |||
166 | config MTD_NAND_S3C2410_CLKSTOP | 173 | config MTD_NAND_S3C2410_CLKSTOP |
167 | bool "S3C2410 NAND IDLE clock stop" | 174 | bool "S3C2410 NAND IDLE clock stop" |
168 | depends on MTD_NAND_S3C2410 | 175 | depends on MTD_NAND_S3C2410 |
diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index 955959eb02d4..582cf80f555a 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c | |||
@@ -2,12 +2,20 @@ | |||
2 | * drivers/mtd/ndfc.c | 2 | * drivers/mtd/ndfc.c |
3 | * | 3 | * |
4 | * Overview: | 4 | * Overview: |
5 | * Platform independend driver for NDFC (NanD Flash Controller) | 5 | * Platform independent driver for NDFC (NanD Flash Controller) |
6 | * integrated into EP440 cores | 6 | * integrated into EP440 cores |
7 | * | 7 | * |
8 | * Ported to an OF platform driver by Sean MacLennan | ||
9 | * | ||
10 | * The NDFC supports multiple chips, but this driver only supports a | ||
11 | * single chip since I do not have access to any boards with | ||
12 | * multiple chips. | ||
13 | * | ||
8 | * Author: Thomas Gleixner | 14 | * Author: Thomas Gleixner |
9 | * | 15 | * |
10 | * Copyright 2006 IBM | 16 | * Copyright 2006 IBM |
17 | * Copyright 2008 PIKA Technologies | ||
18 | * Sean MacLennan <smaclennan@pikatech.com> | ||
11 | * | 19 | * |
12 | * This program is free software; you can redistribute it and/or modify it | 20 | * This program is free software; you can redistribute it and/or modify it |
13 | * under the terms of the GNU General Public License as published by the | 21 | * under the terms of the GNU General Public License as published by the |
@@ -21,27 +29,20 @@ | |||
21 | #include <linux/mtd/partitions.h> | 29 | #include <linux/mtd/partitions.h> |
22 | #include <linux/mtd/ndfc.h> | 30 | #include <linux/mtd/ndfc.h> |
23 | #include <linux/mtd/mtd.h> | 31 | #include <linux/mtd/mtd.h> |
24 | #include <linux/platform_device.h> | 32 | #include <linux/of_platform.h> |
25 | |||
26 | #include <asm/io.h> | 33 | #include <asm/io.h> |
27 | #ifdef CONFIG_40x | ||
28 | #include <asm/ibm405.h> | ||
29 | #else | ||
30 | #include <asm/ibm44x.h> | ||
31 | #endif | ||
32 | |||
33 | struct ndfc_nand_mtd { | ||
34 | struct mtd_info mtd; | ||
35 | struct nand_chip chip; | ||
36 | struct platform_nand_chip *pl_chip; | ||
37 | }; | ||
38 | 34 | ||
39 | static struct ndfc_nand_mtd ndfc_mtd[NDFC_MAX_BANKS]; | ||
40 | 35 | ||
41 | struct ndfc_controller { | 36 | struct ndfc_controller { |
42 | void __iomem *ndfcbase; | 37 | struct of_device *ofdev; |
43 | struct nand_hw_control ndfc_control; | 38 | void __iomem *ndfcbase; |
44 | atomic_t childs_active; | 39 | struct mtd_info mtd; |
40 | struct nand_chip chip; | ||
41 | int chip_select; | ||
42 | struct nand_hw_control ndfc_control; | ||
43 | #ifdef CONFIG_MTD_PARTITIONS | ||
44 | struct mtd_partition *parts; | ||
45 | #endif | ||
45 | }; | 46 | }; |
46 | 47 | ||
47 | static struct ndfc_controller ndfc_ctrl; | 48 | static struct ndfc_controller ndfc_ctrl; |
@@ -50,17 +51,14 @@ static void ndfc_select_chip(struct mtd_info *mtd, int chip) | |||
50 | { | 51 | { |
51 | uint32_t ccr; | 52 | uint32_t ccr; |
52 | struct ndfc_controller *ndfc = &ndfc_ctrl; | 53 | struct ndfc_controller *ndfc = &ndfc_ctrl; |
53 | struct nand_chip *nandchip = mtd->priv; | ||
54 | struct ndfc_nand_mtd *nandmtd = nandchip->priv; | ||
55 | struct platform_nand_chip *pchip = nandmtd->pl_chip; | ||
56 | 54 | ||
57 | ccr = __raw_readl(ndfc->ndfcbase + NDFC_CCR); | 55 | ccr = in_be32(ndfc->ndfcbase + NDFC_CCR); |
58 | if (chip >= 0) { | 56 | if (chip >= 0) { |
59 | ccr &= ~NDFC_CCR_BS_MASK; | 57 | ccr &= ~NDFC_CCR_BS_MASK; |
60 | ccr |= NDFC_CCR_BS(chip + pchip->chip_offset); | 58 | ccr |= NDFC_CCR_BS(chip + ndfc->chip_select); |
61 | } else | 59 | } else |
62 | ccr |= NDFC_CCR_RESET_CE; | 60 | ccr |= NDFC_CCR_RESET_CE; |
63 | __raw_writel(ccr, ndfc->ndfcbase + NDFC_CCR); | 61 | out_be32(ndfc->ndfcbase + NDFC_CCR, ccr); |
64 | } | 62 | } |
65 | 63 | ||
66 | static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) | 64 | static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) |
@@ -80,7 +78,7 @@ static int ndfc_ready(struct mtd_info *mtd) | |||
80 | { | 78 | { |
81 | struct ndfc_controller *ndfc = &ndfc_ctrl; | 79 | struct ndfc_controller *ndfc = &ndfc_ctrl; |
82 | 80 | ||
83 | return __raw_readl(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY; | 81 | return in_be32(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY; |
84 | } | 82 | } |
85 | 83 | ||
86 | static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode) | 84 | static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode) |
@@ -88,9 +86,9 @@ static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode) | |||
88 | uint32_t ccr; | 86 | uint32_t ccr; |
89 | struct ndfc_controller *ndfc = &ndfc_ctrl; | 87 | struct ndfc_controller *ndfc = &ndfc_ctrl; |
90 | 88 | ||
91 | ccr = __raw_readl(ndfc->ndfcbase + NDFC_CCR); | 89 | ccr = in_be32(ndfc->ndfcbase + NDFC_CCR); |
92 | ccr |= NDFC_CCR_RESET_ECC; | 90 | ccr |= NDFC_CCR_RESET_ECC; |
93 | __raw_writel(ccr, ndfc->ndfcbase + NDFC_CCR); | 91 | out_be32(ndfc->ndfcbase + NDFC_CCR, ccr); |
94 | wmb(); | 92 | wmb(); |
95 | } | 93 | } |
96 | 94 | ||
@@ -102,9 +100,10 @@ static int ndfc_calculate_ecc(struct mtd_info *mtd, | |||
102 | uint8_t *p = (uint8_t *)&ecc; | 100 | uint8_t *p = (uint8_t *)&ecc; |
103 | 101 | ||
104 | wmb(); | 102 | wmb(); |
105 | ecc = __raw_readl(ndfc->ndfcbase + NDFC_ECC); | 103 | ecc = in_be32(ndfc->ndfcbase + NDFC_ECC); |
106 | ecc_code[0] = p[1]; | 104 | /* The NDFC uses Smart Media (SMC) bytes order */ |
107 | ecc_code[1] = p[2]; | 105 | ecc_code[0] = p[2]; |
106 | ecc_code[1] = p[1]; | ||
108 | ecc_code[2] = p[3]; | 107 | ecc_code[2] = p[3]; |
109 | 108 | ||
110 | return 0; | 109 | return 0; |
@@ -123,7 +122,7 @@ static void ndfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) | |||
123 | uint32_t *p = (uint32_t *) buf; | 122 | uint32_t *p = (uint32_t *) buf; |
124 | 123 | ||
125 | for(;len > 0; len -= 4) | 124 | for(;len > 0; len -= 4) |
126 | *p++ = __raw_readl(ndfc->ndfcbase + NDFC_DATA); | 125 | *p++ = in_be32(ndfc->ndfcbase + NDFC_DATA); |
127 | } | 126 | } |
128 | 127 | ||
129 | static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) | 128 | static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) |
@@ -132,7 +131,7 @@ static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) | |||
132 | uint32_t *p = (uint32_t *) buf; | 131 | uint32_t *p = (uint32_t *) buf; |
133 | 132 | ||
134 | for(;len > 0; len -= 4) | 133 | for(;len > 0; len -= 4) |
135 | __raw_writel(*p++, ndfc->ndfcbase + NDFC_DATA); | 134 | out_be32(ndfc->ndfcbase + NDFC_DATA, *p++); |
136 | } | 135 | } |
137 | 136 | ||
138 | static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) | 137 | static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) |
@@ -141,7 +140,7 @@ static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) | |||
141 | uint32_t *p = (uint32_t *) buf; | 140 | uint32_t *p = (uint32_t *) buf; |
142 | 141 | ||
143 | for(;len > 0; len -= 4) | 142 | for(;len > 0; len -= 4) |
144 | if (*p++ != __raw_readl(ndfc->ndfcbase + NDFC_DATA)) | 143 | if (*p++ != in_be32(ndfc->ndfcbase + NDFC_DATA)) |
145 | return -EFAULT; | 144 | return -EFAULT; |
146 | return 0; | 145 | return 0; |
147 | } | 146 | } |
@@ -149,10 +148,19 @@ static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) | |||
149 | /* | 148 | /* |
150 | * Initialize chip structure | 149 | * Initialize chip structure |
151 | */ | 150 | */ |
152 | static void ndfc_chip_init(struct ndfc_nand_mtd *mtd) | 151 | static int ndfc_chip_init(struct ndfc_controller *ndfc, |
152 | struct device_node *node) | ||
153 | { | 153 | { |
154 | struct ndfc_controller *ndfc = &ndfc_ctrl; | 154 | #ifdef CONFIG_MTD_PARTITIONS |
155 | struct nand_chip *chip = &mtd->chip; | 155 | #ifdef CONFIG_MTD_CMDLINE_PARTS |
156 | static const char *part_types[] = { "cmdlinepart", NULL }; | ||
157 | #else | ||
158 | static const char *part_types[] = { NULL }; | ||
159 | #endif | ||
160 | #endif | ||
161 | struct device_node *flash_np; | ||
162 | struct nand_chip *chip = &ndfc->chip; | ||
163 | int ret; | ||
156 | 164 | ||
157 | chip->IO_ADDR_R = ndfc->ndfcbase + NDFC_DATA; | 165 | chip->IO_ADDR_R = ndfc->ndfcbase + NDFC_DATA; |
158 | chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA; | 166 | chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA; |
@@ -160,8 +168,6 @@ static void ndfc_chip_init(struct ndfc_nand_mtd *mtd) | |||
160 | chip->dev_ready = ndfc_ready; | 168 | chip->dev_ready = ndfc_ready; |
161 | chip->select_chip = ndfc_select_chip; | 169 | chip->select_chip = ndfc_select_chip; |
162 | chip->chip_delay = 50; | 170 | chip->chip_delay = 50; |
163 | chip->priv = mtd; | ||
164 | chip->options = mtd->pl_chip->options; | ||
165 | chip->controller = &ndfc->ndfc_control; | 171 | chip->controller = &ndfc->ndfc_control; |
166 | chip->read_buf = ndfc_read_buf; | 172 | chip->read_buf = ndfc_read_buf; |
167 | chip->write_buf = ndfc_write_buf; | 173 | chip->write_buf = ndfc_write_buf; |
@@ -172,143 +178,136 @@ static void ndfc_chip_init(struct ndfc_nand_mtd *mtd) | |||
172 | chip->ecc.mode = NAND_ECC_HW; | 178 | chip->ecc.mode = NAND_ECC_HW; |
173 | chip->ecc.size = 256; | 179 | chip->ecc.size = 256; |
174 | chip->ecc.bytes = 3; | 180 | chip->ecc.bytes = 3; |
175 | chip->ecclayout = chip->ecc.layout = mtd->pl_chip->ecclayout; | ||
176 | mtd->mtd.priv = chip; | ||
177 | mtd->mtd.owner = THIS_MODULE; | ||
178 | } | ||
179 | |||
180 | static int ndfc_chip_probe(struct platform_device *pdev) | ||
181 | { | ||
182 | struct platform_nand_chip *nc = pdev->dev.platform_data; | ||
183 | struct ndfc_chip_settings *settings = nc->priv; | ||
184 | struct ndfc_controller *ndfc = &ndfc_ctrl; | ||
185 | struct ndfc_nand_mtd *nandmtd; | ||
186 | |||
187 | if (nc->chip_offset >= NDFC_MAX_BANKS || nc->nr_chips > NDFC_MAX_BANKS) | ||
188 | return -EINVAL; | ||
189 | |||
190 | /* Set the bank settings */ | ||
191 | __raw_writel(settings->bank_settings, | ||
192 | ndfc->ndfcbase + NDFC_BCFG0 + (nc->chip_offset << 2)); | ||
193 | 181 | ||
194 | nandmtd = &ndfc_mtd[pdev->id]; | 182 | ndfc->mtd.priv = chip; |
195 | if (nandmtd->pl_chip) | 183 | ndfc->mtd.owner = THIS_MODULE; |
196 | return -EBUSY; | ||
197 | 184 | ||
198 | nandmtd->pl_chip = nc; | 185 | flash_np = of_get_next_child(node, NULL); |
199 | ndfc_chip_init(nandmtd); | 186 | if (!flash_np) |
200 | |||
201 | /* Scan for chips */ | ||
202 | if (nand_scan(&nandmtd->mtd, nc->nr_chips)) { | ||
203 | nandmtd->pl_chip = NULL; | ||
204 | return -ENODEV; | 187 | return -ENODEV; |
188 | |||
189 | ndfc->mtd.name = kasprintf(GFP_KERNEL, "%s.%s", | ||
190 | ndfc->ofdev->dev.bus_id, flash_np->name); | ||
191 | if (!ndfc->mtd.name) { | ||
192 | ret = -ENOMEM; | ||
193 | goto err; | ||
205 | } | 194 | } |
206 | 195 | ||
207 | #ifdef CONFIG_MTD_PARTITIONS | 196 | ret = nand_scan(&ndfc->mtd, 1); |
208 | printk("Number of partitions %d\n", nc->nr_partitions); | 197 | if (ret) |
209 | if (nc->nr_partitions) { | 198 | goto err; |
210 | /* Add the full device, so complete dumps can be made */ | ||
211 | add_mtd_device(&nandmtd->mtd); | ||
212 | add_mtd_partitions(&nandmtd->mtd, nc->partitions, | ||
213 | nc->nr_partitions); | ||
214 | 199 | ||
215 | } else | 200 | #ifdef CONFIG_MTD_PARTITIONS |
216 | #else | 201 | ret = parse_mtd_partitions(&ndfc->mtd, part_types, &ndfc->parts, 0); |
217 | add_mtd_device(&nandmtd->mtd); | 202 | if (ret < 0) |
203 | goto err; | ||
204 | |||
205 | #ifdef CONFIG_MTD_OF_PARTS | ||
206 | if (ret == 0) { | ||
207 | ret = of_mtd_parse_partitions(&ndfc->ofdev->dev, flash_np, | ||
208 | &ndfc->parts); | ||
209 | if (ret < 0) | ||
210 | goto err; | ||
211 | } | ||
218 | #endif | 212 | #endif |
219 | 213 | ||
220 | atomic_inc(&ndfc->childs_active); | 214 | if (ret > 0) |
221 | return 0; | 215 | ret = add_mtd_partitions(&ndfc->mtd, ndfc->parts, ret); |
222 | } | 216 | else |
217 | #endif | ||
218 | ret = add_mtd_device(&ndfc->mtd); | ||
223 | 219 | ||
224 | static int ndfc_chip_remove(struct platform_device *pdev) | 220 | err: |
225 | { | 221 | of_node_put(flash_np); |
226 | return 0; | 222 | if (ret) |
223 | kfree(ndfc->mtd.name); | ||
224 | return ret; | ||
227 | } | 225 | } |
228 | 226 | ||
229 | static int ndfc_nand_probe(struct platform_device *pdev) | 227 | static int __devinit ndfc_probe(struct of_device *ofdev, |
228 | const struct of_device_id *match) | ||
230 | { | 229 | { |
231 | struct platform_nand_ctrl *nc = pdev->dev.platform_data; | ||
232 | struct ndfc_controller_settings *settings = nc->priv; | ||
233 | struct resource *res = pdev->resource; | ||
234 | struct ndfc_controller *ndfc = &ndfc_ctrl; | 230 | struct ndfc_controller *ndfc = &ndfc_ctrl; |
235 | unsigned long long phys = settings->ndfc_erpn | res->start; | 231 | const u32 *reg; |
232 | u32 ccr; | ||
233 | int err, len; | ||
236 | 234 | ||
237 | #ifndef CONFIG_PHYS_64BIT | 235 | spin_lock_init(&ndfc->ndfc_control.lock); |
238 | ndfc->ndfcbase = ioremap((phys_addr_t)phys, res->end - res->start + 1); | 236 | init_waitqueue_head(&ndfc->ndfc_control.wq); |
239 | #else | 237 | ndfc->ofdev = ofdev; |
240 | ndfc->ndfcbase = ioremap64(phys, res->end - res->start + 1); | 238 | dev_set_drvdata(&ofdev->dev, ndfc); |
241 | #endif | 239 | |
240 | /* Read the reg property to get the chip select */ | ||
241 | reg = of_get_property(ofdev->node, "reg", &len); | ||
242 | if (reg == NULL || len != 12) { | ||
243 | dev_err(&ofdev->dev, "unable read reg property (%d)\n", len); | ||
244 | return -ENOENT; | ||
245 | } | ||
246 | ndfc->chip_select = reg[0]; | ||
247 | |||
248 | ndfc->ndfcbase = of_iomap(ofdev->node, 0); | ||
242 | if (!ndfc->ndfcbase) { | 249 | if (!ndfc->ndfcbase) { |
243 | printk(KERN_ERR "NDFC: ioremap failed\n"); | 250 | dev_err(&ofdev->dev, "failed to get memory\n"); |
244 | return -EIO; | 251 | return -EIO; |
245 | } | 252 | } |
246 | 253 | ||
247 | __raw_writel(settings->ccr_settings, ndfc->ndfcbase + NDFC_CCR); | 254 | ccr = NDFC_CCR_BS(ndfc->chip_select); |
248 | 255 | ||
249 | spin_lock_init(&ndfc->ndfc_control.lock); | 256 | /* It is ok if ccr does not exist - just default to 0 */ |
250 | init_waitqueue_head(&ndfc->ndfc_control.wq); | 257 | reg = of_get_property(ofdev->node, "ccr", NULL); |
258 | if (reg) | ||
259 | ccr |= *reg; | ||
251 | 260 | ||
252 | platform_set_drvdata(pdev, ndfc); | 261 | out_be32(ndfc->ndfcbase + NDFC_CCR, ccr); |
253 | 262 | ||
254 | printk("NDFC NAND Driver initialized. Chip-Rev: 0x%08x\n", | 263 | /* Set the bank settings if given */ |
255 | __raw_readl(ndfc->ndfcbase + NDFC_REVID)); | 264 | reg = of_get_property(ofdev->node, "bank-settings", NULL); |
265 | if (reg) { | ||
266 | int offset = NDFC_BCFG0 + (ndfc->chip_select << 2); | ||
267 | out_be32(ndfc->ndfcbase + offset, *reg); | ||
268 | } | ||
269 | |||
270 | err = ndfc_chip_init(ndfc, ofdev->node); | ||
271 | if (err) { | ||
272 | iounmap(ndfc->ndfcbase); | ||
273 | return err; | ||
274 | } | ||
256 | 275 | ||
257 | return 0; | 276 | return 0; |
258 | } | 277 | } |
259 | 278 | ||
260 | static int ndfc_nand_remove(struct platform_device *pdev) | 279 | static int __devexit ndfc_remove(struct of_device *ofdev) |
261 | { | 280 | { |
262 | struct ndfc_controller *ndfc = platform_get_drvdata(pdev); | 281 | struct ndfc_controller *ndfc = dev_get_drvdata(&ofdev->dev); |
263 | 282 | ||
264 | if (atomic_read(&ndfc->childs_active)) | 283 | nand_release(&ndfc->mtd); |
265 | return -EBUSY; | ||
266 | 284 | ||
267 | if (ndfc) { | ||
268 | platform_set_drvdata(pdev, NULL); | ||
269 | iounmap(ndfc_ctrl.ndfcbase); | ||
270 | ndfc_ctrl.ndfcbase = NULL; | ||
271 | } | ||
272 | return 0; | 285 | return 0; |
273 | } | 286 | } |
274 | 287 | ||
275 | /* driver device registration */ | 288 | static const struct of_device_id ndfc_match[] = { |
276 | 289 | { .compatible = "ibm,ndfc", }, | |
277 | static struct platform_driver ndfc_chip_driver = { | 290 | {} |
278 | .probe = ndfc_chip_probe, | ||
279 | .remove = ndfc_chip_remove, | ||
280 | .driver = { | ||
281 | .name = "ndfc-chip", | ||
282 | .owner = THIS_MODULE, | ||
283 | }, | ||
284 | }; | 291 | }; |
292 | MODULE_DEVICE_TABLE(of, ndfc_match); | ||
285 | 293 | ||
286 | static struct platform_driver ndfc_nand_driver = { | 294 | static struct of_platform_driver ndfc_driver = { |
287 | .probe = ndfc_nand_probe, | 295 | .driver = { |
288 | .remove = ndfc_nand_remove, | 296 | .name = "ndfc", |
289 | .driver = { | ||
290 | .name = "ndfc-nand", | ||
291 | .owner = THIS_MODULE, | ||
292 | }, | 297 | }, |
298 | .match_table = ndfc_match, | ||
299 | .probe = ndfc_probe, | ||
300 | .remove = __devexit_p(ndfc_remove), | ||
293 | }; | 301 | }; |
294 | 302 | ||
295 | static int __init ndfc_nand_init(void) | 303 | static int __init ndfc_nand_init(void) |
296 | { | 304 | { |
297 | int ret; | 305 | return of_register_platform_driver(&ndfc_driver); |
298 | |||
299 | spin_lock_init(&ndfc_ctrl.ndfc_control.lock); | ||
300 | init_waitqueue_head(&ndfc_ctrl.ndfc_control.wq); | ||
301 | |||
302 | ret = platform_driver_register(&ndfc_nand_driver); | ||
303 | if (!ret) | ||
304 | ret = platform_driver_register(&ndfc_chip_driver); | ||
305 | return ret; | ||
306 | } | 306 | } |
307 | 307 | ||
308 | static void __exit ndfc_nand_exit(void) | 308 | static void __exit ndfc_nand_exit(void) |
309 | { | 309 | { |
310 | platform_driver_unregister(&ndfc_chip_driver); | 310 | of_unregister_platform_driver(&ndfc_driver); |
311 | platform_driver_unregister(&ndfc_nand_driver); | ||
312 | } | 311 | } |
313 | 312 | ||
314 | module_init(ndfc_nand_init); | 313 | module_init(ndfc_nand_init); |
@@ -316,6 +315,4 @@ module_exit(ndfc_nand_exit); | |||
316 | 315 | ||
317 | MODULE_LICENSE("GPL"); | 316 | MODULE_LICENSE("GPL"); |
318 | MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>"); | 317 | MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>"); |
319 | MODULE_DESCRIPTION("Platform driver for NDFC"); | 318 | MODULE_DESCRIPTION("OF Platform driver for NDFC"); |
320 | MODULE_ALIAS("platform:ndfc-chip"); | ||
321 | MODULE_ALIAS("platform:ndfc-nand"); | ||