blob: 5130a853102418399b1e20f601c9d07525c5e437 [file] [log] [blame]
Sascha Hauer34f6e152008-09-02 17:16:59 +02001/*
2 * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
3 * Copyright 2008 Sascha Hauer, kernel@pengutronix.de
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 * MA 02110-1301, USA.
18 */
19
20#include <linux/delay.h>
21#include <linux/slab.h>
22#include <linux/init.h>
23#include <linux/module.h>
24#include <linux/mtd/mtd.h>
25#include <linux/mtd/nand.h>
26#include <linux/mtd/partitions.h>
27#include <linux/interrupt.h>
28#include <linux/device.h>
29#include <linux/platform_device.h>
30#include <linux/clk.h>
31#include <linux/err.h>
32#include <linux/io.h>
33
34#include <asm/mach/flash.h>
35#include <mach/mxc_nand.h>
Sascha Hauer94671142009-10-05 12:14:21 +020036#include <mach/hardware.h>
Sascha Hauer34f6e152008-09-02 17:16:59 +020037
38#define DRIVER_NAME "mxc_nand"
39
Sascha Hauer94671142009-10-05 12:14:21 +020040#define nfc_is_v21() (cpu_is_mx25() || cpu_is_mx35())
Ivo Claryssea47bfd22010-04-08 16:16:51 +020041#define nfc_is_v1() (cpu_is_mx31() || cpu_is_mx27() || cpu_is_mx21())
Sascha Hauer94671142009-10-05 12:14:21 +020042
Sascha Hauer34f6e152008-09-02 17:16:59 +020043/* Addresses for NFC registers */
Sascha Hauer938cf992010-08-06 15:53:04 +020044#define NFC_BUF_SIZE 0x00
45#define NFC_BUF_ADDR 0x04
46#define NFC_FLASH_ADDR 0x06
47#define NFC_FLASH_CMD 0x08
48#define NFC_CONFIG 0x0a
49#define NFC_ECC_STATUS_RESULT 0x0c
50#define NFC_RSLTMAIN_AREA 0x0e
51#define NFC_RSLTSPARE_AREA 0x10
52#define NFC_WRPROT 0x12
53#define NFC_V1_UNLOCKSTART_BLKADDR 0x14
54#define NFC_V1_UNLOCKEND_BLKADDR 0x16
55#define NFC_V21_UNLOCKSTART_BLKADDR 0x20
56#define NFC_V21_UNLOCKEND_BLKADDR 0x22
57#define NFC_NF_WRPRST 0x18
58#define NFC_CONFIG1 0x1a
59#define NFC_CONFIG2 0x1c
Sascha Hauer34f6e152008-09-02 17:16:59 +020060
Sascha Hauer34f6e152008-09-02 17:16:59 +020061/* Set INT to 0, FCMD to 1, rest to 0 in NFC_CONFIG2 Register
62 * for Command operation */
63#define NFC_CMD 0x1
64
65/* Set INT to 0, FADD to 1, rest to 0 in NFC_CONFIG2 Register
66 * for Address operation */
67#define NFC_ADDR 0x2
68
69/* Set INT to 0, FDI to 1, rest to 0 in NFC_CONFIG2 Register
70 * for Input operation */
71#define NFC_INPUT 0x4
72
73/* Set INT to 0, FDO to 001, rest to 0 in NFC_CONFIG2 Register
74 * for Data Output operation */
75#define NFC_OUTPUT 0x8
76
77/* Set INT to 0, FD0 to 010, rest to 0 in NFC_CONFIG2 Register
78 * for Read ID operation */
79#define NFC_ID 0x10
80
81/* Set INT to 0, FDO to 100, rest to 0 in NFC_CONFIG2 Register
82 * for Read Status operation */
83#define NFC_STATUS 0x20
84
85/* Set INT to 1, rest to 0 in NFC_CONFIG2 Register for Read
86 * Status operation */
87#define NFC_INT 0x8000
88
89#define NFC_SP_EN (1 << 2)
90#define NFC_ECC_EN (1 << 3)
91#define NFC_INT_MSK (1 << 4)
92#define NFC_BIG (1 << 5)
93#define NFC_RST (1 << 6)
94#define NFC_CE (1 << 7)
95#define NFC_ONE_CYCLE (1 << 8)
96
97struct mxc_nand_host {
98 struct mtd_info mtd;
99 struct nand_chip nand;
100 struct mtd_partition *parts;
101 struct device *dev;
102
Sascha Hauerc6de7e12009-10-05 11:14:35 +0200103 void *spare0;
104 void *main_area0;
Sascha Hauerc6de7e12009-10-05 11:14:35 +0200105
106 void __iomem *base;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200107 void __iomem *regs;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200108 int status_request;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200109 struct clk *clk;
110 int clk_act;
111 int irq;
112
113 wait_queue_head_t irq_waitq;
Sascha Hauerf8f96082009-06-04 17:12:26 +0200114
115 uint8_t *data_buf;
116 unsigned int buf_start;
117 int spare_len;
Sascha Hauer5f973042010-08-06 15:53:06 +0200118
119 void (*preset)(struct mtd_info *);
120 void (*send_cmd)(struct mxc_nand_host *, uint16_t, int);
121 void (*send_addr)(struct mxc_nand_host *, uint16_t, int);
122 void (*send_page)(struct mtd_info *, unsigned int);
123 void (*send_read_id)(struct mxc_nand_host *);
124 uint16_t (*get_dev_status)(struct mxc_nand_host *);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200125};
126
Sascha Hauer34f6e152008-09-02 17:16:59 +0200127/* OOB placement block for use with hardware ecc generation */
Sascha Hauer94671142009-10-05 12:14:21 +0200128static struct nand_ecclayout nandv1_hw_eccoob_smallpage = {
Sascha Hauer34f6e152008-09-02 17:16:59 +0200129 .eccbytes = 5,
130 .eccpos = {6, 7, 8, 9, 10},
Sascha Hauer8c1fd892009-10-21 10:22:01 +0200131 .oobfree = {{0, 5}, {12, 4}, }
Sascha Hauer34f6e152008-09-02 17:16:59 +0200132};
133
Sascha Hauer94671142009-10-05 12:14:21 +0200134static struct nand_ecclayout nandv1_hw_eccoob_largepage = {
Vladimir Barinovbd3fd622009-05-25 13:06:17 +0400135 .eccbytes = 20,
136 .eccpos = {6, 7, 8, 9, 10, 22, 23, 24, 25, 26,
137 38, 39, 40, 41, 42, 54, 55, 56, 57, 58},
138 .oobfree = {{2, 4}, {11, 10}, {27, 10}, {43, 10}, {59, 5}, }
Sascha Hauer34f6e152008-09-02 17:16:59 +0200139};
140
Sascha Hauer94671142009-10-05 12:14:21 +0200141/* OOB description for 512 byte pages with 16 byte OOB */
142static struct nand_ecclayout nandv2_hw_eccoob_smallpage = {
143 .eccbytes = 1 * 9,
144 .eccpos = {
145 7, 8, 9, 10, 11, 12, 13, 14, 15
146 },
147 .oobfree = {
148 {.offset = 0, .length = 5}
149 }
150};
151
152/* OOB description for 2048 byte pages with 64 byte OOB */
153static struct nand_ecclayout nandv2_hw_eccoob_largepage = {
154 .eccbytes = 4 * 9,
155 .eccpos = {
156 7, 8, 9, 10, 11, 12, 13, 14, 15,
157 23, 24, 25, 26, 27, 28, 29, 30, 31,
158 39, 40, 41, 42, 43, 44, 45, 46, 47,
159 55, 56, 57, 58, 59, 60, 61, 62, 63
160 },
161 .oobfree = {
162 {.offset = 2, .length = 4},
163 {.offset = 16, .length = 7},
164 {.offset = 32, .length = 7},
165 {.offset = 48, .length = 7}
166 }
167};
168
Sascha Hauer34f6e152008-09-02 17:16:59 +0200169#ifdef CONFIG_MTD_PARTITIONS
170static const char *part_probes[] = { "RedBoot", "cmdlinepart", NULL };
171#endif
172
173static irqreturn_t mxc_nfc_irq(int irq, void *dev_id)
174{
175 struct mxc_nand_host *host = dev_id;
176
Ivo Claryssea47bfd22010-04-08 16:16:51 +0200177 disable_irq_nosync(irq);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200178
179 wake_up(&host->irq_waitq);
180
181 return IRQ_HANDLED;
182}
183
184/* This function polls the NANDFC to wait for the basic operation to
185 * complete by checking the INT bit of config2 register.
186 */
Sascha Hauerc110eaf2009-10-21 16:01:02 +0200187static void wait_op_done(struct mxc_nand_host *host, int useirq)
Sascha Hauer34f6e152008-09-02 17:16:59 +0200188{
Ivo Claryssea47bfd22010-04-08 16:16:51 +0200189 uint16_t tmp;
190 int max_retries = 8000;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200191
192 if (useirq) {
193 if ((readw(host->regs + NFC_CONFIG2) & NFC_INT) == 0) {
194
Ivo Claryssea47bfd22010-04-08 16:16:51 +0200195 enable_irq(host->irq);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200196
197 wait_event(host->irq_waitq,
198 readw(host->regs + NFC_CONFIG2) & NFC_INT);
199
200 tmp = readw(host->regs + NFC_CONFIG2);
201 tmp &= ~NFC_INT;
202 writew(tmp, host->regs + NFC_CONFIG2);
203 }
204 } else {
205 while (max_retries-- > 0) {
206 if (readw(host->regs + NFC_CONFIG2) & NFC_INT) {
207 tmp = readw(host->regs + NFC_CONFIG2);
208 tmp &= ~NFC_INT;
209 writew(tmp, host->regs + NFC_CONFIG2);
210 break;
211 }
212 udelay(1);
213 }
Roel Kluin43950a62009-06-04 16:24:59 +0200214 if (max_retries < 0)
Sascha Hauer62465492009-06-04 15:57:20 +0200215 DEBUG(MTD_DEBUG_LEVEL0, "%s: INT not set\n",
216 __func__);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200217 }
218}
219
220/* This function issues the specified command to the NAND device and
221 * waits for completion. */
Sascha Hauer5f973042010-08-06 15:53:06 +0200222static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq)
Sascha Hauer34f6e152008-09-02 17:16:59 +0200223{
224 DEBUG(MTD_DEBUG_LEVEL3, "send_cmd(host, 0x%x, %d)\n", cmd, useirq);
225
226 writew(cmd, host->regs + NFC_FLASH_CMD);
227 writew(NFC_CMD, host->regs + NFC_CONFIG2);
228
Ivo Claryssea47bfd22010-04-08 16:16:51 +0200229 if (cpu_is_mx21() && (cmd == NAND_CMD_RESET)) {
230 int max_retries = 100;
231 /* Reset completion is indicated by NFC_CONFIG2 */
232 /* being set to 0 */
233 while (max_retries-- > 0) {
234 if (readw(host->regs + NFC_CONFIG2) == 0) {
235 break;
236 }
237 udelay(1);
238 }
239 if (max_retries < 0)
240 DEBUG(MTD_DEBUG_LEVEL0, "%s: RESET failed\n",
241 __func__);
242 } else {
243 /* Wait for operation to complete */
244 wait_op_done(host, useirq);
245 }
Sascha Hauer34f6e152008-09-02 17:16:59 +0200246}
247
248/* This function sends an address (or partial address) to the
249 * NAND device. The address is used to select the source/destination for
250 * a NAND command. */
Sascha Hauer5f973042010-08-06 15:53:06 +0200251static void send_addr_v1_v2(struct mxc_nand_host *host, uint16_t addr, int islast)
Sascha Hauer34f6e152008-09-02 17:16:59 +0200252{
253 DEBUG(MTD_DEBUG_LEVEL3, "send_addr(host, 0x%x %d)\n", addr, islast);
254
255 writew(addr, host->regs + NFC_FLASH_ADDR);
256 writew(NFC_ADDR, host->regs + NFC_CONFIG2);
257
258 /* Wait for operation to complete */
Sascha Hauerc110eaf2009-10-21 16:01:02 +0200259 wait_op_done(host, islast);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200260}
261
Sascha Hauer5f973042010-08-06 15:53:06 +0200262static void send_page_v1_v2(struct mtd_info *mtd, unsigned int ops)
Sascha Hauer34f6e152008-09-02 17:16:59 +0200263{
Sascha Hauer2d69c7f2009-10-05 11:24:02 +0200264 struct nand_chip *nand_chip = mtd->priv;
265 struct mxc_nand_host *host = nand_chip->priv;
Sascha Hauerc5d23f12009-06-04 17:25:53 +0200266 int bufs, i;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200267
Sascha Hauer94671142009-10-05 12:14:21 +0200268 if (nfc_is_v1() && mtd->writesize > 512)
Sascha Hauerc5d23f12009-06-04 17:25:53 +0200269 bufs = 4;
270 else
271 bufs = 1;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200272
Sascha Hauerc5d23f12009-06-04 17:25:53 +0200273 for (i = 0; i < bufs; i++) {
274
275 /* NANDFC buffer 0 is used for page read/write */
276 writew(i, host->regs + NFC_BUF_ADDR);
277
278 writew(ops, host->regs + NFC_CONFIG2);
279
280 /* Wait for operation to complete */
Sascha Hauerc110eaf2009-10-21 16:01:02 +0200281 wait_op_done(host, true);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200282 }
Sascha Hauer34f6e152008-09-02 17:16:59 +0200283}
284
285/* Request the NANDFC to perform a read of the NAND device ID. */
Sascha Hauer5f973042010-08-06 15:53:06 +0200286static void send_read_id_v1_v2(struct mxc_nand_host *host)
Sascha Hauer34f6e152008-09-02 17:16:59 +0200287{
288 struct nand_chip *this = &host->nand;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200289
290 /* NANDFC buffer 0 is used for device ID output */
291 writew(0x0, host->regs + NFC_BUF_ADDR);
292
Sascha Hauer34f6e152008-09-02 17:16:59 +0200293 writew(NFC_ID, host->regs + NFC_CONFIG2);
294
295 /* Wait for operation to complete */
Sascha Hauerc110eaf2009-10-21 16:01:02 +0200296 wait_op_done(host, true);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200297
298 if (this->options & NAND_BUSWIDTH_16) {
Sascha Hauerc6de7e12009-10-05 11:14:35 +0200299 void __iomem *main_buf = host->main_area0;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200300 /* compress the ID info */
301 writeb(readb(main_buf + 2), main_buf + 1);
302 writeb(readb(main_buf + 4), main_buf + 2);
303 writeb(readb(main_buf + 6), main_buf + 3);
304 writeb(readb(main_buf + 8), main_buf + 4);
305 writeb(readb(main_buf + 10), main_buf + 5);
306 }
Sascha Hauerc6de7e12009-10-05 11:14:35 +0200307 memcpy(host->data_buf, host->main_area0, 16);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200308}
309
310/* This function requests the NANDFC to perform a read of the
311 * NAND device status and returns the current status. */
Sascha Hauer5f973042010-08-06 15:53:06 +0200312static uint16_t get_dev_status_v1_v2(struct mxc_nand_host *host)
Sascha Hauer34f6e152008-09-02 17:16:59 +0200313{
Sascha Hauerc29c6072010-08-06 15:53:05 +0200314 void __iomem *main_buf = host->main_area0;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200315 uint32_t store;
Sascha Hauerf06368f2009-10-05 17:18:42 +0200316 uint16_t ret;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200317
Sascha Hauerc29c6072010-08-06 15:53:05 +0200318 writew(0x0, NFC_V1_V2_BUF_ADDR);
319
320 /*
321 * The device status is stored in main_area0. To
322 * prevent corruption of the buffer save the value
323 * and restore it afterwards.
324 */
Sascha Hauer34f6e152008-09-02 17:16:59 +0200325 store = readl(main_buf);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200326
Sascha Hauer34f6e152008-09-02 17:16:59 +0200327 writew(NFC_STATUS, host->regs + NFC_CONFIG2);
Sascha Hauerc110eaf2009-10-21 16:01:02 +0200328 wait_op_done(host, true);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200329
Sascha Hauer34f6e152008-09-02 17:16:59 +0200330 ret = readw(main_buf);
Sascha Hauerc29c6072010-08-06 15:53:05 +0200331
Sascha Hauer34f6e152008-09-02 17:16:59 +0200332 writel(store, main_buf);
333
334 return ret;
335}
336
337/* This functions is used by upper layer to checks if device is ready */
338static int mxc_nand_dev_ready(struct mtd_info *mtd)
339{
340 /*
341 * NFC handles R/B internally. Therefore, this function
342 * always returns status as ready.
343 */
344 return 1;
345}
346
347static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode)
348{
349 /*
350 * If HW ECC is enabled, we turn it on during init. There is
351 * no need to enable again here.
352 */
353}
354
355static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat,
356 u_char *read_ecc, u_char *calc_ecc)
357{
358 struct nand_chip *nand_chip = mtd->priv;
359 struct mxc_nand_host *host = nand_chip->priv;
360
361 /*
362 * 1-Bit errors are automatically corrected in HW. No need for
363 * additional correction. 2-Bit errors cannot be corrected by
364 * HW ECC, so we need to return failure
365 */
366 uint16_t ecc_status = readw(host->regs + NFC_ECC_STATUS_RESULT);
367
368 if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) {
369 DEBUG(MTD_DEBUG_LEVEL0,
370 "MXC_NAND: HWECC uncorrectable 2-bit ECC error\n");
371 return -1;
372 }
373
374 return 0;
375}
376
377static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
378 u_char *ecc_code)
379{
380 return 0;
381}
382
383static u_char mxc_nand_read_byte(struct mtd_info *mtd)
384{
385 struct nand_chip *nand_chip = mtd->priv;
386 struct mxc_nand_host *host = nand_chip->priv;
Sascha Hauerf8f96082009-06-04 17:12:26 +0200387 uint8_t ret;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200388
389 /* Check for status request */
390 if (host->status_request)
Sascha Hauer5f973042010-08-06 15:53:06 +0200391 return host->get_dev_status(host) & 0xFF;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200392
Sascha Hauerf8f96082009-06-04 17:12:26 +0200393 ret = *(uint8_t *)(host->data_buf + host->buf_start);
394 host->buf_start++;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200395
396 return ret;
397}
398
399static uint16_t mxc_nand_read_word(struct mtd_info *mtd)
400{
401 struct nand_chip *nand_chip = mtd->priv;
402 struct mxc_nand_host *host = nand_chip->priv;
Sascha Hauerf8f96082009-06-04 17:12:26 +0200403 uint16_t ret;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200404
Sascha Hauerf8f96082009-06-04 17:12:26 +0200405 ret = *(uint16_t *)(host->data_buf + host->buf_start);
406 host->buf_start += 2;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200407
408 return ret;
409}
410
411/* Write data of length len to buffer buf. The data to be
412 * written on NAND Flash is first copied to RAMbuffer. After the Data Input
413 * Operation by the NFC, the data is written to NAND Flash */
414static void mxc_nand_write_buf(struct mtd_info *mtd,
415 const u_char *buf, int len)
416{
417 struct nand_chip *nand_chip = mtd->priv;
418 struct mxc_nand_host *host = nand_chip->priv;
Sascha Hauerf8f96082009-06-04 17:12:26 +0200419 u16 col = host->buf_start;
420 int n = mtd->oobsize + mtd->writesize - col;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200421
Sascha Hauerf8f96082009-06-04 17:12:26 +0200422 n = min(n, len);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200423
Sascha Hauerf8f96082009-06-04 17:12:26 +0200424 memcpy(host->data_buf + col, buf, n);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200425
Sascha Hauerf8f96082009-06-04 17:12:26 +0200426 host->buf_start += n;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200427}
428
429/* Read the data buffer from the NAND Flash. To read the data from NAND
430 * Flash first the data output cycle is initiated by the NFC, which copies
431 * the data to RAMbuffer. This data of length len is then copied to buffer buf.
432 */
433static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
434{
435 struct nand_chip *nand_chip = mtd->priv;
436 struct mxc_nand_host *host = nand_chip->priv;
Sascha Hauerf8f96082009-06-04 17:12:26 +0200437 u16 col = host->buf_start;
438 int n = mtd->oobsize + mtd->writesize - col;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200439
Sascha Hauerf8f96082009-06-04 17:12:26 +0200440 n = min(n, len);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200441
Sascha Hauerf8f96082009-06-04 17:12:26 +0200442 memcpy(buf, host->data_buf + col, len);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200443
Sascha Hauerf8f96082009-06-04 17:12:26 +0200444 host->buf_start += len;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200445}
446
447/* Used by the upper layer to verify the data in NAND Flash
448 * with the data in the buf. */
449static int mxc_nand_verify_buf(struct mtd_info *mtd,
450 const u_char *buf, int len)
451{
452 return -EFAULT;
453}
454
455/* This function is used by upper layer for select and
456 * deselect of the NAND chip */
457static void mxc_nand_select_chip(struct mtd_info *mtd, int chip)
458{
459 struct nand_chip *nand_chip = mtd->priv;
460 struct mxc_nand_host *host = nand_chip->priv;
461
Sascha Hauer34f6e152008-09-02 17:16:59 +0200462 switch (chip) {
463 case -1:
464 /* Disable the NFC clock */
465 if (host->clk_act) {
466 clk_disable(host->clk);
467 host->clk_act = 0;
468 }
469 break;
470 case 0:
471 /* Enable the NFC clock */
472 if (!host->clk_act) {
473 clk_enable(host->clk);
474 host->clk_act = 1;
475 }
476 break;
477
478 default:
479 break;
480 }
481}
482
Sascha Hauerf8f96082009-06-04 17:12:26 +0200483/*
484 * Function to transfer data to/from spare area.
485 */
486static void copy_spare(struct mtd_info *mtd, bool bfrom)
487{
488 struct nand_chip *this = mtd->priv;
489 struct mxc_nand_host *host = this->priv;
490 u16 i, j;
491 u16 n = mtd->writesize >> 9;
492 u8 *d = host->data_buf + mtd->writesize;
Sascha Hauerc6de7e12009-10-05 11:14:35 +0200493 u8 *s = host->spare0;
Sascha Hauerf8f96082009-06-04 17:12:26 +0200494 u16 t = host->spare_len;
495
496 j = (mtd->oobsize / n >> 1) << 1;
497
498 if (bfrom) {
499 for (i = 0; i < n - 1; i++)
500 memcpy(d + i * j, s + i * t, j);
501
502 /* the last section */
503 memcpy(d + i * j, s + i * t, mtd->oobsize - i * j);
504 } else {
505 for (i = 0; i < n - 1; i++)
506 memcpy(&s[i * t], &d[i * j], j);
507
508 /* the last section */
509 memcpy(&s[i * t], &d[i * j], mtd->oobsize - i * j);
510 }
511}
512
Sascha Hauera3e65b62009-06-02 11:47:59 +0200513static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr)
Sascha Hauer34f6e152008-09-02 17:16:59 +0200514{
515 struct nand_chip *nand_chip = mtd->priv;
516 struct mxc_nand_host *host = nand_chip->priv;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200517
518 /* Write out column address, if necessary */
519 if (column != -1) {
520 /*
521 * MXC NANDFC can only perform full page+spare or
522 * spare-only read/write. When the upper layers
523 * layers perform a read/write buf operation,
Daniel Mack3ad2f3f2010-02-03 08:01:28 +0800524 * we will used the saved column address to index into
Sascha Hauer34f6e152008-09-02 17:16:59 +0200525 * the full page.
526 */
Sascha Hauer5f973042010-08-06 15:53:06 +0200527 host->send_addr(host, 0, page_addr == -1);
Sascha Hauer2d69c7f2009-10-05 11:24:02 +0200528 if (mtd->writesize > 512)
Sascha Hauer34f6e152008-09-02 17:16:59 +0200529 /* another col addr cycle for 2k page */
Sascha Hauer5f973042010-08-06 15:53:06 +0200530 host->send_addr(host, 0, false);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200531 }
532
533 /* Write out page address, if necessary */
534 if (page_addr != -1) {
535 /* paddr_0 - p_addr_7 */
Sascha Hauer5f973042010-08-06 15:53:06 +0200536 host->send_addr(host, (page_addr & 0xff), false);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200537
Sascha Hauer2d69c7f2009-10-05 11:24:02 +0200538 if (mtd->writesize > 512) {
Vladimir Barinovbd3fd622009-05-25 13:06:17 +0400539 if (mtd->size >= 0x10000000) {
540 /* paddr_8 - paddr_15 */
Sascha Hauer5f973042010-08-06 15:53:06 +0200541 host->send_addr(host, (page_addr >> 8) & 0xff, false);
542 host->send_addr(host, (page_addr >> 16) & 0xff, true);
Vladimir Barinovbd3fd622009-05-25 13:06:17 +0400543 } else
544 /* paddr_8 - paddr_15 */
Sascha Hauer5f973042010-08-06 15:53:06 +0200545 host->send_addr(host, (page_addr >> 8) & 0xff, true);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200546 } else {
547 /* One more address cycle for higher density devices */
548 if (mtd->size >= 0x4000000) {
549 /* paddr_8 - paddr_15 */
Sascha Hauer5f973042010-08-06 15:53:06 +0200550 host->send_addr(host, (page_addr >> 8) & 0xff, false);
551 host->send_addr(host, (page_addr >> 16) & 0xff, true);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200552 } else
553 /* paddr_8 - paddr_15 */
Sascha Hauer5f973042010-08-06 15:53:06 +0200554 host->send_addr(host, (page_addr >> 8) & 0xff, true);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200555 }
556 }
Sascha Hauera3e65b62009-06-02 11:47:59 +0200557}
Sascha Hauer34f6e152008-09-02 17:16:59 +0200558
Sascha Hauer5f973042010-08-06 15:53:06 +0200559static void preset_v1_v2(struct mtd_info *mtd)
Ivo Claryssed4840182010-04-08 16:14:44 +0200560{
561 struct nand_chip *nand_chip = mtd->priv;
562 struct mxc_nand_host *host = nand_chip->priv;
563 uint16_t tmp;
564
Ivo Claryssea47bfd22010-04-08 16:16:51 +0200565 /* enable interrupt, disable spare enable */
Ivo Claryssed4840182010-04-08 16:14:44 +0200566 tmp = readw(host->regs + NFC_CONFIG1);
Ivo Claryssea47bfd22010-04-08 16:16:51 +0200567 tmp &= ~NFC_INT_MSK;
Ivo Claryssed4840182010-04-08 16:14:44 +0200568 tmp &= ~NFC_SP_EN;
569 if (nand_chip->ecc.mode == NAND_ECC_HW) {
570 tmp |= NFC_ECC_EN;
571 } else {
572 tmp &= ~NFC_ECC_EN;
573 }
574 writew(tmp, host->regs + NFC_CONFIG1);
575 /* preset operation */
576
577 /* Unlock the internal RAM Buffer */
578 writew(0x2, host->regs + NFC_CONFIG);
579
580 /* Blocks to be unlocked */
581 if (nfc_is_v21()) {
582 writew(0x0, host->regs + NFC_V21_UNLOCKSTART_BLKADDR);
583 writew(0xffff, host->regs + NFC_V21_UNLOCKEND_BLKADDR);
584 } else if (nfc_is_v1()) {
585 writew(0x0, host->regs + NFC_V1_UNLOCKSTART_BLKADDR);
586 writew(0x4000, host->regs + NFC_V1_UNLOCKEND_BLKADDR);
587 } else
588 BUG();
589
590 /* Unlock Block Command for given address range */
591 writew(0x4, host->regs + NFC_WRPROT);
592}
593
Sascha Hauer34f6e152008-09-02 17:16:59 +0200594/* Used by the upper layer to write command to NAND Flash for
595 * different operations to be carried out on NAND Flash */
596static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
597 int column, int page_addr)
598{
599 struct nand_chip *nand_chip = mtd->priv;
600 struct mxc_nand_host *host = nand_chip->priv;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200601
602 DEBUG(MTD_DEBUG_LEVEL3,
603 "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n",
604 command, column, page_addr);
605
606 /* Reset command state information */
607 host->status_request = false;
608
609 /* Command pre-processing step */
Sascha Hauer34f6e152008-09-02 17:16:59 +0200610 switch (command) {
Ivo Claryssed4840182010-04-08 16:14:44 +0200611 case NAND_CMD_RESET:
Sascha Hauer5f973042010-08-06 15:53:06 +0200612 host->preset(mtd);
613 host->send_cmd(host, command, false);
Ivo Claryssed4840182010-04-08 16:14:44 +0200614 break;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200615
Sascha Hauer34f6e152008-09-02 17:16:59 +0200616 case NAND_CMD_STATUS:
Sascha Hauerf8f96082009-06-04 17:12:26 +0200617 host->buf_start = 0;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200618 host->status_request = true;
Sascha Hauer89121a62009-06-04 17:18:01 +0200619
Sascha Hauer5f973042010-08-06 15:53:06 +0200620 host->send_cmd(host, command, true);
Sascha Hauer89121a62009-06-04 17:18:01 +0200621 mxc_do_addr_cycle(mtd, column, page_addr);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200622 break;
623
Sascha Hauer34f6e152008-09-02 17:16:59 +0200624 case NAND_CMD_READ0:
Sascha Hauer34f6e152008-09-02 17:16:59 +0200625 case NAND_CMD_READOOB:
Sascha Hauer89121a62009-06-04 17:18:01 +0200626 if (command == NAND_CMD_READ0)
627 host->buf_start = column;
628 else
629 host->buf_start = column + mtd->writesize;
Sascha Hauerf8f96082009-06-04 17:12:26 +0200630
Sascha Hauer6c499392010-05-28 10:02:17 +0200631 command = NAND_CMD_READ0; /* only READ0 is valid */
Sascha Hauer89121a62009-06-04 17:18:01 +0200632
Sascha Hauer5f973042010-08-06 15:53:06 +0200633 host->send_cmd(host, command, false);
Sascha Hauer89121a62009-06-04 17:18:01 +0200634 mxc_do_addr_cycle(mtd, column, page_addr);
635
Sascha Hauer2d69c7f2009-10-05 11:24:02 +0200636 if (mtd->writesize > 512)
Sascha Hauer5f973042010-08-06 15:53:06 +0200637 host->send_cmd(host, NAND_CMD_READSTART, true);
Sascha Hauerc5d23f12009-06-04 17:25:53 +0200638
Sascha Hauer5f973042010-08-06 15:53:06 +0200639 host->send_page(mtd, NFC_OUTPUT);
Sascha Hauer89121a62009-06-04 17:18:01 +0200640
Sascha Hauerc6de7e12009-10-05 11:14:35 +0200641 memcpy(host->data_buf, host->main_area0, mtd->writesize);
Sascha Hauer89121a62009-06-04 17:18:01 +0200642 copy_spare(mtd, true);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200643 break;
644
Sascha Hauer34f6e152008-09-02 17:16:59 +0200645 case NAND_CMD_SEQIN:
Sascha Hauer6c499392010-05-28 10:02:17 +0200646 if (column >= mtd->writesize)
647 /* call ourself to read a page */
648 mxc_nand_command(mtd, NAND_CMD_READ0, 0, page_addr);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200649
Sascha Hauer6c499392010-05-28 10:02:17 +0200650 host->buf_start = column;
Sascha Hauer89121a62009-06-04 17:18:01 +0200651
Sascha Hauer5f973042010-08-06 15:53:06 +0200652 host->send_cmd(host, command, false);
Sascha Hauer89121a62009-06-04 17:18:01 +0200653 mxc_do_addr_cycle(mtd, column, page_addr);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200654 break;
655
656 case NAND_CMD_PAGEPROG:
Sascha Hauerc6de7e12009-10-05 11:14:35 +0200657 memcpy(host->main_area0, host->data_buf, mtd->writesize);
Sascha Hauerf8f96082009-06-04 17:12:26 +0200658 copy_spare(mtd, false);
Sascha Hauer5f973042010-08-06 15:53:06 +0200659 host->send_page(mtd, NFC_INPUT);
660 host->send_cmd(host, command, true);
Sascha Hauer89121a62009-06-04 17:18:01 +0200661 mxc_do_addr_cycle(mtd, column, page_addr);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200662 break;
663
Sascha Hauer34f6e152008-09-02 17:16:59 +0200664 case NAND_CMD_READID:
Sascha Hauer5f973042010-08-06 15:53:06 +0200665 host->send_cmd(host, command, true);
Sascha Hauer89121a62009-06-04 17:18:01 +0200666 mxc_do_addr_cycle(mtd, column, page_addr);
Sascha Hauer5f973042010-08-06 15:53:06 +0200667 host->send_read_id(host);
Sascha Hauer94671142009-10-05 12:14:21 +0200668 host->buf_start = column;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200669 break;
670
Sascha Hauer89121a62009-06-04 17:18:01 +0200671 case NAND_CMD_ERASE1:
Sascha Hauer34f6e152008-09-02 17:16:59 +0200672 case NAND_CMD_ERASE2:
Sascha Hauer5f973042010-08-06 15:53:06 +0200673 host->send_cmd(host, command, false);
Sascha Hauer89121a62009-06-04 17:18:01 +0200674 mxc_do_addr_cycle(mtd, column, page_addr);
675
Sascha Hauer34f6e152008-09-02 17:16:59 +0200676 break;
677 }
678}
679
Sascha Hauerf1372052009-10-21 14:25:27 +0200680/*
681 * The generic flash bbt decriptors overlap with our ecc
682 * hardware, so define some i.MX specific ones.
683 */
684static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' };
685static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' };
686
687static struct nand_bbt_descr bbt_main_descr = {
688 .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
689 | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
690 .offs = 0,
691 .len = 4,
692 .veroffs = 4,
693 .maxblocks = 4,
694 .pattern = bbt_pattern,
695};
696
697static struct nand_bbt_descr bbt_mirror_descr = {
698 .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
699 | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
700 .offs = 0,
701 .len = 4,
702 .veroffs = 4,
703 .maxblocks = 4,
704 .pattern = mirror_pattern,
705};
706
Sascha Hauer34f6e152008-09-02 17:16:59 +0200707static int __init mxcnd_probe(struct platform_device *pdev)
708{
709 struct nand_chip *this;
710 struct mtd_info *mtd;
711 struct mxc_nand_platform_data *pdata = pdev->dev.platform_data;
712 struct mxc_nand_host *host;
713 struct resource *res;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200714 int err = 0, nr_parts = 0;
Sascha Hauer94671142009-10-05 12:14:21 +0200715 struct nand_ecclayout *oob_smallpage, *oob_largepage;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200716
717 /* Allocate memory for MTD device structure and private data */
Sascha Hauerf8f96082009-06-04 17:12:26 +0200718 host = kzalloc(sizeof(struct mxc_nand_host) + NAND_MAX_PAGESIZE +
719 NAND_MAX_OOBSIZE, GFP_KERNEL);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200720 if (!host)
721 return -ENOMEM;
722
Sascha Hauerf8f96082009-06-04 17:12:26 +0200723 host->data_buf = (uint8_t *)(host + 1);
Sascha Hauerf8f96082009-06-04 17:12:26 +0200724
Sascha Hauer34f6e152008-09-02 17:16:59 +0200725 host->dev = &pdev->dev;
726 /* structures must be linked */
727 this = &host->nand;
728 mtd = &host->mtd;
729 mtd->priv = this;
730 mtd->owner = THIS_MODULE;
David Brownell87f39f02009-03-26 00:42:50 -0700731 mtd->dev.parent = &pdev->dev;
Sascha Hauer1fbff0a2009-10-21 16:06:27 +0200732 mtd->name = DRIVER_NAME;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200733
734 /* 50 us command delay time */
735 this->chip_delay = 5;
736
737 this->priv = host;
738 this->dev_ready = mxc_nand_dev_ready;
739 this->cmdfunc = mxc_nand_command;
740 this->select_chip = mxc_nand_select_chip;
741 this->read_byte = mxc_nand_read_byte;
742 this->read_word = mxc_nand_read_word;
743 this->write_buf = mxc_nand_write_buf;
744 this->read_buf = mxc_nand_read_buf;
745 this->verify_buf = mxc_nand_verify_buf;
746
Sascha Hauere65fb002009-02-16 14:29:10 +0100747 host->clk = clk_get(&pdev->dev, "nfc");
Vladimir Barinov8541c112009-04-23 15:47:22 +0400748 if (IS_ERR(host->clk)) {
749 err = PTR_ERR(host->clk);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200750 goto eclk;
Vladimir Barinov8541c112009-04-23 15:47:22 +0400751 }
Sascha Hauer34f6e152008-09-02 17:16:59 +0200752
753 clk_enable(host->clk);
754 host->clk_act = 1;
755
756 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
757 if (!res) {
758 err = -ENODEV;
759 goto eres;
760 }
761
Sascha Hauerc6de7e12009-10-05 11:14:35 +0200762 host->base = ioremap(res->start, resource_size(res));
763 if (!host->base) {
Vladimir Barinov8541c112009-04-23 15:47:22 +0400764 err = -ENOMEM;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200765 goto eres;
766 }
767
Sascha Hauerc6de7e12009-10-05 11:14:35 +0200768 host->main_area0 = host->base;
Sascha Hauer94671142009-10-05 12:14:21 +0200769
Sascha Hauer5f973042010-08-06 15:53:06 +0200770 if (nfc_is_v1() || nfc_is_v21()) {
771 host->preset = preset_v1_v2;
772 host->send_cmd = send_cmd_v1_v2;
773 host->send_addr = send_addr_v1_v2;
774 host->send_page = send_page_v1_v2;
775 host->send_read_id = send_read_id_v1_v2;
776 host->get_dev_status = get_dev_status_v1_v2;
777 }
778
Sascha Hauer94671142009-10-05 12:14:21 +0200779 if (nfc_is_v21()) {
Sascha Hauer938cf992010-08-06 15:53:04 +0200780 host->regs = host->base + 0x1e00;
Sascha Hauer94671142009-10-05 12:14:21 +0200781 host->spare0 = host->base + 0x1000;
782 host->spare_len = 64;
783 oob_smallpage = &nandv2_hw_eccoob_smallpage;
784 oob_largepage = &nandv2_hw_eccoob_largepage;
Ivo Claryssed4840182010-04-08 16:14:44 +0200785 this->ecc.bytes = 9;
Sascha Hauer94671142009-10-05 12:14:21 +0200786 } else if (nfc_is_v1()) {
Sascha Hauer938cf992010-08-06 15:53:04 +0200787 host->regs = host->base + 0xe00;
Sascha Hauer94671142009-10-05 12:14:21 +0200788 host->spare0 = host->base + 0x800;
789 host->spare_len = 16;
790 oob_smallpage = &nandv1_hw_eccoob_smallpage;
791 oob_largepage = &nandv1_hw_eccoob_largepage;
Sascha Hauer94671142009-10-05 12:14:21 +0200792 this->ecc.bytes = 3;
793 } else
794 BUG();
Sascha Hauer34f6e152008-09-02 17:16:59 +0200795
Sascha Hauer13e1add2009-10-21 10:39:05 +0200796 this->ecc.size = 512;
Sascha Hauer94671142009-10-05 12:14:21 +0200797 this->ecc.layout = oob_smallpage;
Sascha Hauer13e1add2009-10-21 10:39:05 +0200798
799 if (pdata->hw_ecc) {
800 this->ecc.calculate = mxc_nand_calculate_ecc;
801 this->ecc.hwctl = mxc_nand_enable_hwecc;
802 this->ecc.correct = mxc_nand_correct_data;
803 this->ecc.mode = NAND_ECC_HW;
Sascha Hauer13e1add2009-10-21 10:39:05 +0200804 } else {
805 this->ecc.mode = NAND_ECC_SOFT;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200806 }
807
Sascha Hauer34f6e152008-09-02 17:16:59 +0200808 /* NAND bus width determines access funtions used by upper layer */
Sascha Hauer13e1add2009-10-21 10:39:05 +0200809 if (pdata->width == 2)
Sascha Hauer34f6e152008-09-02 17:16:59 +0200810 this->options |= NAND_BUSWIDTH_16;
Sascha Hauer13e1add2009-10-21 10:39:05 +0200811
Sascha Hauerf1372052009-10-21 14:25:27 +0200812 if (pdata->flash_bbt) {
813 this->bbt_td = &bbt_main_descr;
814 this->bbt_md = &bbt_mirror_descr;
815 /* update flash based bbt */
816 this->options |= NAND_USE_FLASH_BBT;
Sascha Hauer34f6e152008-09-02 17:16:59 +0200817 }
818
Ivo Claryssed4840182010-04-08 16:14:44 +0200819 init_waitqueue_head(&host->irq_waitq);
820
821 host->irq = platform_get_irq(pdev, 0);
822
Ivo Claryssea47bfd22010-04-08 16:16:51 +0200823 err = request_irq(host->irq, mxc_nfc_irq, IRQF_DISABLED, DRIVER_NAME, host);
Ivo Claryssed4840182010-04-08 16:14:44 +0200824 if (err)
825 goto eirq;
826
Vladimir Barinovbd3fd622009-05-25 13:06:17 +0400827 /* first scan to find the device and get the page size */
David Woodhouse5e81e882010-02-26 18:32:56 +0000828 if (nand_scan_ident(mtd, 1, NULL)) {
Vladimir Barinovbd3fd622009-05-25 13:06:17 +0400829 err = -ENXIO;
830 goto escan;
831 }
Sascha Hauer34f6e152008-09-02 17:16:59 +0200832
Sascha Hauer2d69c7f2009-10-05 11:24:02 +0200833 if (mtd->writesize == 2048)
Sascha Hauer94671142009-10-05 12:14:21 +0200834 this->ecc.layout = oob_largepage;
Vladimir Barinovbd3fd622009-05-25 13:06:17 +0400835
836 /* second phase scan */
837 if (nand_scan_tail(mtd)) {
Sascha Hauer34f6e152008-09-02 17:16:59 +0200838 err = -ENXIO;
839 goto escan;
840 }
841
842 /* Register the partitions */
843#ifdef CONFIG_MTD_PARTITIONS
844 nr_parts =
845 parse_mtd_partitions(mtd, part_probes, &host->parts, 0);
846 if (nr_parts > 0)
847 add_mtd_partitions(mtd, host->parts, nr_parts);
848 else
849#endif
850 {
851 pr_info("Registering %s as whole device\n", mtd->name);
852 add_mtd_device(mtd);
853 }
854
855 platform_set_drvdata(pdev, host);
856
857 return 0;
858
859escan:
Magnus Liljab258fd82009-05-08 21:57:47 +0200860 free_irq(host->irq, host);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200861eirq:
Sascha Hauerc6de7e12009-10-05 11:14:35 +0200862 iounmap(host->base);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200863eres:
864 clk_put(host->clk);
865eclk:
866 kfree(host);
867
868 return err;
869}
870
Uwe Kleine-König51eeb872009-12-07 09:44:05 +0000871static int __devexit mxcnd_remove(struct platform_device *pdev)
Sascha Hauer34f6e152008-09-02 17:16:59 +0200872{
873 struct mxc_nand_host *host = platform_get_drvdata(pdev);
874
875 clk_put(host->clk);
876
877 platform_set_drvdata(pdev, NULL);
878
879 nand_release(&host->mtd);
Magnus Liljab258fd82009-05-08 21:57:47 +0200880 free_irq(host->irq, host);
Sascha Hauerc6de7e12009-10-05 11:14:35 +0200881 iounmap(host->base);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200882 kfree(host);
883
884 return 0;
885}
886
Sascha Hauer34f6e152008-09-02 17:16:59 +0200887static struct platform_driver mxcnd_driver = {
888 .driver = {
889 .name = DRIVER_NAME,
Eric Bénard04dd0d32010-06-17 20:59:04 +0200890 },
Uwe Kleine-Königdaa0f152009-11-24 22:07:08 +0100891 .remove = __devexit_p(mxcnd_remove),
Sascha Hauer34f6e152008-09-02 17:16:59 +0200892};
893
894static int __init mxc_nd_init(void)
895{
Vladimir Barinov8541c112009-04-23 15:47:22 +0400896 return platform_driver_probe(&mxcnd_driver, mxcnd_probe);
Sascha Hauer34f6e152008-09-02 17:16:59 +0200897}
898
899static void __exit mxc_nd_cleanup(void)
900{
901 /* Unregister the device structure */
902 platform_driver_unregister(&mxcnd_driver);
903}
904
905module_init(mxc_nd_init);
906module_exit(mxc_nd_cleanup);
907
908MODULE_AUTHOR("Freescale Semiconductor, Inc.");
909MODULE_DESCRIPTION("MXC NAND MTD driver");
910MODULE_LICENSE("GPL");