]>
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 | * | |
1a459660 | 7 | * SPDX-License-Identifier: GPL-2.0+ |
cd9d2305 AV |
8 | */ |
9 | ||
10 | #include <config.h> | |
cd9d2305 AV |
11 | #include <common.h> |
12 | #include <asm/io.h> | |
1221ce45 | 13 | #include <linux/errno.h> |
cd9d2305 AV |
14 | #include <linux/mtd/mtd.h> |
15 | #include <linux/mtd/fsl_upm.h> | |
16 | #include <nand.h> | |
17 | ||
cd9d2305 AV |
18 | static void fsl_upm_start_pattern(struct fsl_upm *upm, u32 pat_offset) |
19 | { | |
a75a57ef | 20 | clrsetbits_be32(upm->mxmr, MxMR_MAD_MSK, MxMR_OP_RUNP | pat_offset); |
9fd84915 | 21 | (void)in_be32(upm->mxmr); |
cd9d2305 AV |
22 | } |
23 | ||
24 | static void fsl_upm_end_pattern(struct fsl_upm *upm) | |
25 | { | |
a75a57ef WG |
26 | clrbits_be32(upm->mxmr, MxMR_OP_RUNP); |
27 | ||
28 | while (in_be32(upm->mxmr) & MxMR_OP_RUNP) | |
cd9d2305 AV |
29 | eieio(); |
30 | } | |
31 | ||
e93c1c16 WG |
32 | static void fsl_upm_run_pattern(struct fsl_upm *upm, int width, |
33 | void __iomem *io_addr, u32 mar) | |
cd9d2305 | 34 | { |
e93c1c16 | 35 | out_be32(upm->mar, mar); |
9fd84915 | 36 | (void)in_be32(upm->mar); |
a75a57ef WG |
37 | switch (width) { |
38 | case 8: | |
e93c1c16 | 39 | out_8(io_addr, 0x0); |
a75a57ef WG |
40 | break; |
41 | case 16: | |
e93c1c16 | 42 | out_be16(io_addr, 0x0); |
a75a57ef WG |
43 | break; |
44 | case 32: | |
e93c1c16 | 45 | out_be32(io_addr, 0x0); |
a75a57ef | 46 | break; |
cd9d2305 | 47 | } |
cd9d2305 AV |
48 | } |
49 | ||
33846df2 WG |
50 | static void fun_wait(struct fsl_upm_nand *fun) |
51 | { | |
52 | if (fun->dev_ready) { | |
53 | while (!fun->dev_ready(fun->chip_nr)) | |
54 | debug("unexpected busy state\n"); | |
55 | } else { | |
56 | /* | |
d923a5d5 | 57 | * If the R/B pin is not connected, |
33846df2 WG |
58 | * a short delay is necessary. |
59 | */ | |
60 | udelay(1); | |
61 | } | |
62 | } | |
63 | ||
e93c1c16 WG |
64 | #if CONFIG_SYS_NAND_MAX_CHIPS > 1 |
65 | static void fun_select_chip(struct mtd_info *mtd, int chip_nr) | |
66 | { | |
17cb4b8f SW |
67 | struct nand_chip *chip = mtd_to_nand(mtd); |
68 | struct fsl_upm_nand *fun = nand_get_controller_data(chip); | |
e93c1c16 WG |
69 | |
70 | if (chip_nr >= 0) { | |
71 | fun->chip_nr = chip_nr; | |
72 | chip->IO_ADDR_R = chip->IO_ADDR_W = | |
73 | fun->upm.io_addr + fun->chip_offset * chip_nr; | |
74 | } else if (chip_nr == -1) { | |
75 | chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); | |
76 | } | |
77 | } | |
78 | #endif | |
79 | ||
e1c3dbad | 80 | static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) |
cd9d2305 | 81 | { |
17cb4b8f SW |
82 | struct nand_chip *chip = mtd_to_nand(mtd); |
83 | struct fsl_upm_nand *fun = nand_get_controller_data(chip); | |
e93c1c16 WG |
84 | void __iomem *io_addr; |
85 | u32 mar; | |
cd9d2305 | 86 | |
e1c3dbad | 87 | if (!(ctrl & fun->last_ctrl)) { |
a75a57ef | 88 | fsl_upm_end_pattern(&fun->upm); |
e1c3dbad AV |
89 | |
90 | if (cmd == NAND_CMD_NONE) | |
91 | return; | |
92 | ||
93 | fun->last_ctrl = ctrl & (NAND_ALE | NAND_CLE); | |
cd9d2305 AV |
94 | } |
95 | ||
e1c3dbad AV |
96 | if (ctrl & NAND_CTRL_CHANGE) { |
97 | if (ctrl & NAND_ALE) | |
98 | fsl_upm_start_pattern(&fun->upm, fun->upm_addr_offset); | |
99 | else if (ctrl & NAND_CLE) | |
100 | fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset); | |
101 | } | |
cd9d2305 | 102 | |
e93c1c16 WG |
103 | mar = cmd << (32 - fun->width); |
104 | io_addr = fun->upm.io_addr; | |
105 | #if CONFIG_SYS_NAND_MAX_CHIPS > 1 | |
06e9f7df | 106 | if (fun->chip_nr > 0) { |
e93c1c16 | 107 | io_addr += fun->chip_offset * fun->chip_nr; |
06e9f7df WG |
108 | if (fun->upm_mar_chip_offset) |
109 | mar |= fun->upm_mar_chip_offset * fun->chip_nr; | |
110 | } | |
e93c1c16 WG |
111 | #endif |
112 | fsl_upm_run_pattern(&fun->upm, fun->width, io_addr, mar); | |
e1c3dbad AV |
113 | |
114 | /* | |
93e14596 WD |
115 | * Some boards/chips needs this. At least the MPC8360E-RDK |
116 | * needs it. Probably weird chip, because I don't see any | |
117 | * need for this on MPC8555E + Samsung K9F1G08U0A. Usually | |
118 | * here are 0-2 unexpected busy states per block read. | |
e1c3dbad | 119 | */ |
33846df2 WG |
120 | if (fun->wait_flags & FSL_UPM_WAIT_RUN_PATTERN) |
121 | fun_wait(fun); | |
cd9d2305 AV |
122 | } |
123 | ||
24dd863f | 124 | static u8 upm_nand_read_byte(struct mtd_info *mtd) |
cd9d2305 | 125 | { |
17cb4b8f | 126 | struct nand_chip *chip = mtd_to_nand(mtd); |
cd9d2305 AV |
127 | |
128 | return in_8(chip->IO_ADDR_R); | |
129 | } | |
130 | ||
24dd863f | 131 | static void upm_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) |
cd9d2305 AV |
132 | { |
133 | int i; | |
17cb4b8f SW |
134 | struct nand_chip *chip = mtd_to_nand(mtd); |
135 | struct fsl_upm_nand *fun = nand_get_controller_data(chip); | |
cd9d2305 | 136 | |
33846df2 | 137 | for (i = 0; i < len; i++) { |
cd9d2305 | 138 | out_8(chip->IO_ADDR_W, buf[i]); |
33846df2 WG |
139 | if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BYTE) |
140 | fun_wait(fun); | |
141 | } | |
142 | ||
143 | if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BUFFER) | |
144 | fun_wait(fun); | |
cd9d2305 AV |
145 | } |
146 | ||
24dd863f | 147 | static void upm_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) |
cd9d2305 AV |
148 | { |
149 | int i; | |
17cb4b8f | 150 | struct nand_chip *chip = mtd_to_nand(mtd); |
cd9d2305 AV |
151 | |
152 | for (i = 0; i < len; i++) | |
153 | buf[i] = in_8(chip->IO_ADDR_R); | |
154 | } | |
155 | ||
cd9d2305 AV |
156 | static int nand_dev_ready(struct mtd_info *mtd) |
157 | { | |
17cb4b8f SW |
158 | struct nand_chip *chip = mtd_to_nand(mtd); |
159 | struct fsl_upm_nand *fun = nand_get_controller_data(chip); | |
cd9d2305 | 160 | |
e93c1c16 | 161 | return fun->dev_ready(fun->chip_nr); |
cd9d2305 AV |
162 | } |
163 | ||
164 | int fsl_upm_nand_init(struct nand_chip *chip, struct fsl_upm_nand *fun) | |
165 | { | |
a75a57ef | 166 | if (fun->width != 8 && fun->width != 16 && fun->width != 32) |
cd9d2305 AV |
167 | return -ENOSYS; |
168 | ||
e1c3dbad AV |
169 | fun->last_ctrl = NAND_CLE; |
170 | ||
17cb4b8f | 171 | nand_set_controller_data(chip, fun); |
cd9d2305 | 172 | chip->chip_delay = fun->chip_delay; |
e1c3dbad AV |
173 | chip->ecc.mode = NAND_ECC_SOFT; |
174 | chip->cmd_ctrl = fun_cmd_ctrl; | |
e93c1c16 WG |
175 | #if CONFIG_SYS_NAND_MAX_CHIPS > 1 |
176 | chip->select_chip = fun_select_chip; | |
177 | #endif | |
24dd863f MV |
178 | chip->read_byte = upm_nand_read_byte; |
179 | chip->read_buf = upm_nand_read_buf; | |
180 | chip->write_buf = upm_nand_write_buf; | |
a75a57ef WG |
181 | if (fun->dev_ready) |
182 | chip->dev_ready = nand_dev_ready; | |
cd9d2305 AV |
183 | |
184 | return 0; | |
185 | } |