]>
Commit | Line | Data |
---|---|---|
cd9d2305 AV |
1 | /* |
2 | * FSL UPM NAND driver | |
3 | * | |
4 | * Copyright (C) 2007 MontaVista Software, Inc. | |
5 | * Anton Vorontsov <avorontsov@ru.mvista.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License as | |
9 | * published by the Free Software Foundation; either version 2 of | |
10 | * the License, or (at your option) any later version. | |
11 | */ | |
12 | ||
13 | #include <config.h> | |
cd9d2305 AV |
14 | #include <common.h> |
15 | #include <asm/io.h> | |
16 | #include <asm/errno.h> | |
17 | #include <linux/mtd/mtd.h> | |
18 | #include <linux/mtd/fsl_upm.h> | |
19 | #include <nand.h> | |
20 | ||
cd9d2305 AV |
21 | static void fsl_upm_start_pattern(struct fsl_upm *upm, u32 pat_offset) |
22 | { | |
a75a57ef | 23 | clrsetbits_be32(upm->mxmr, MxMR_MAD_MSK, MxMR_OP_RUNP | pat_offset); |
cd9d2305 AV |
24 | } |
25 | ||
26 | static void fsl_upm_end_pattern(struct fsl_upm *upm) | |
27 | { | |
a75a57ef WG |
28 | clrbits_be32(upm->mxmr, MxMR_OP_RUNP); |
29 | ||
30 | while (in_be32(upm->mxmr) & MxMR_OP_RUNP) | |
cd9d2305 AV |
31 | eieio(); |
32 | } | |
33 | ||
e93c1c16 WG |
34 | static void fsl_upm_run_pattern(struct fsl_upm *upm, int width, |
35 | void __iomem *io_addr, u32 mar) | |
cd9d2305 | 36 | { |
e93c1c16 | 37 | out_be32(upm->mar, mar); |
a75a57ef WG |
38 | switch (width) { |
39 | case 8: | |
e93c1c16 | 40 | out_8(io_addr, 0x0); |
a75a57ef WG |
41 | break; |
42 | case 16: | |
e93c1c16 | 43 | out_be16(io_addr, 0x0); |
a75a57ef WG |
44 | break; |
45 | case 32: | |
e93c1c16 | 46 | out_be32(io_addr, 0x0); |
a75a57ef | 47 | break; |
cd9d2305 | 48 | } |
cd9d2305 AV |
49 | } |
50 | ||
33846df2 WG |
51 | static void fun_wait(struct fsl_upm_nand *fun) |
52 | { | |
53 | if (fun->dev_ready) { | |
54 | while (!fun->dev_ready(fun->chip_nr)) | |
55 | debug("unexpected busy state\n"); | |
56 | } else { | |
57 | /* | |
58 | * If the R/B pin is not connected, like on the TQM8548, | |
59 | * a short delay is necessary. | |
60 | */ | |
61 | udelay(1); | |
62 | } | |
63 | } | |
64 | ||
e93c1c16 WG |
65 | #if CONFIG_SYS_NAND_MAX_CHIPS > 1 |
66 | static void fun_select_chip(struct mtd_info *mtd, int chip_nr) | |
67 | { | |
68 | struct nand_chip *chip = mtd->priv; | |
69 | struct fsl_upm_nand *fun = chip->priv; | |
70 | ||
71 | if (chip_nr >= 0) { | |
72 | fun->chip_nr = chip_nr; | |
73 | chip->IO_ADDR_R = chip->IO_ADDR_W = | |
74 | fun->upm.io_addr + fun->chip_offset * chip_nr; | |
75 | } else if (chip_nr == -1) { | |
76 | chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); | |
77 | } | |
78 | } | |
79 | #endif | |
80 | ||
e1c3dbad | 81 | static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) |
cd9d2305 AV |
82 | { |
83 | struct nand_chip *chip = mtd->priv; | |
84 | struct fsl_upm_nand *fun = chip->priv; | |
e93c1c16 WG |
85 | void __iomem *io_addr; |
86 | u32 mar; | |
cd9d2305 | 87 | |
e1c3dbad | 88 | if (!(ctrl & fun->last_ctrl)) { |
a75a57ef | 89 | fsl_upm_end_pattern(&fun->upm); |
e1c3dbad AV |
90 | |
91 | if (cmd == NAND_CMD_NONE) | |
92 | return; | |
93 | ||
94 | fun->last_ctrl = ctrl & (NAND_ALE | NAND_CLE); | |
cd9d2305 AV |
95 | } |
96 | ||
e1c3dbad AV |
97 | if (ctrl & NAND_CTRL_CHANGE) { |
98 | if (ctrl & NAND_ALE) | |
99 | fsl_upm_start_pattern(&fun->upm, fun->upm_addr_offset); | |
100 | else if (ctrl & NAND_CLE) | |
101 | fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset); | |
102 | } | |
cd9d2305 | 103 | |
e93c1c16 WG |
104 | mar = cmd << (32 - fun->width); |
105 | io_addr = fun->upm.io_addr; | |
106 | #if CONFIG_SYS_NAND_MAX_CHIPS > 1 | |
06e9f7df | 107 | if (fun->chip_nr > 0) { |
e93c1c16 | 108 | io_addr += fun->chip_offset * fun->chip_nr; |
06e9f7df WG |
109 | if (fun->upm_mar_chip_offset) |
110 | mar |= fun->upm_mar_chip_offset * fun->chip_nr; | |
111 | } | |
e93c1c16 WG |
112 | #endif |
113 | fsl_upm_run_pattern(&fun->upm, fun->width, io_addr, mar); | |
e1c3dbad AV |
114 | |
115 | /* | |
33846df2 WG |
116 | * Some boards/chips needs this. At least the MPC8360E-RDK and |
117 | * TQM8548 need it. Probably weird chip, because I don't see | |
118 | * any need for this on MPC8555E + Samsung K9F1G08U0A. Usually | |
119 | * here are 0-2 unexpected busy states per block read. | |
e1c3dbad | 120 | */ |
33846df2 WG |
121 | if (fun->wait_flags & FSL_UPM_WAIT_RUN_PATTERN) |
122 | fun_wait(fun); | |
cd9d2305 AV |
123 | } |
124 | ||
cd9d2305 AV |
125 | static u8 nand_read_byte(struct mtd_info *mtd) |
126 | { | |
127 | struct nand_chip *chip = mtd->priv; | |
128 | ||
129 | return in_8(chip->IO_ADDR_R); | |
130 | } | |
131 | ||
132 | static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) | |
133 | { | |
134 | int i; | |
135 | struct nand_chip *chip = mtd->priv; | |
33846df2 | 136 | struct fsl_upm_nand *fun = chip->priv; |
cd9d2305 | 137 | |
33846df2 | 138 | for (i = 0; i < len; i++) { |
cd9d2305 | 139 | out_8(chip->IO_ADDR_W, buf[i]); |
33846df2 WG |
140 | if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BYTE) |
141 | fun_wait(fun); | |
142 | } | |
143 | ||
144 | if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BUFFER) | |
145 | fun_wait(fun); | |
cd9d2305 AV |
146 | } |
147 | ||
148 | static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) | |
149 | { | |
150 | int i; | |
151 | struct nand_chip *chip = mtd->priv; | |
152 | ||
153 | for (i = 0; i < len; i++) | |
154 | buf[i] = in_8(chip->IO_ADDR_R); | |
155 | } | |
156 | ||
157 | static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) | |
158 | { | |
159 | int i; | |
160 | struct nand_chip *chip = mtd->priv; | |
161 | ||
162 | for (i = 0; i < len; i++) { | |
163 | if (buf[i] != in_8(chip->IO_ADDR_R)) | |
164 | return -EFAULT; | |
165 | } | |
166 | ||
167 | return 0; | |
168 | } | |
169 | ||
cd9d2305 AV |
170 | static int nand_dev_ready(struct mtd_info *mtd) |
171 | { | |
172 | struct nand_chip *chip = mtd->priv; | |
173 | struct fsl_upm_nand *fun = chip->priv; | |
174 | ||
e93c1c16 | 175 | return fun->dev_ready(fun->chip_nr); |
cd9d2305 AV |
176 | } |
177 | ||
178 | int fsl_upm_nand_init(struct nand_chip *chip, struct fsl_upm_nand *fun) | |
179 | { | |
a75a57ef | 180 | if (fun->width != 8 && fun->width != 16 && fun->width != 32) |
cd9d2305 AV |
181 | return -ENOSYS; |
182 | ||
e1c3dbad AV |
183 | fun->last_ctrl = NAND_CLE; |
184 | ||
cd9d2305 AV |
185 | chip->priv = fun; |
186 | chip->chip_delay = fun->chip_delay; | |
e1c3dbad AV |
187 | chip->ecc.mode = NAND_ECC_SOFT; |
188 | chip->cmd_ctrl = fun_cmd_ctrl; | |
e93c1c16 WG |
189 | #if CONFIG_SYS_NAND_MAX_CHIPS > 1 |
190 | chip->select_chip = fun_select_chip; | |
191 | #endif | |
cd9d2305 AV |
192 | chip->read_byte = nand_read_byte; |
193 | chip->read_buf = nand_read_buf; | |
cd9d2305 AV |
194 | chip->write_buf = nand_write_buf; |
195 | chip->verify_buf = nand_verify_buf; | |
a75a57ef WG |
196 | if (fun->dev_ready) |
197 | chip->dev_ready = nand_dev_ready; | |
cd9d2305 AV |
198 | |
199 | return 0; | |
200 | } |