| @@ -1,514 +1,513 @@ | | | @@ -1,514 +1,513 @@ |
1 | /* $NetBSD: omap2_nand.c,v 1.5 2012/10/27 17:17:40 chs Exp $ */ | | 1 | /* $NetBSD: omap2_nand.c,v 1.6 2016/10/04 15:32:02 kiyohara Exp $ */ |
2 | | | 2 | |
3 | /*- | | 3 | /*- |
4 | * Copyright (c) 2010 Department of Software Engineering, | | 4 | * Copyright (c) 2010 Department of Software Engineering, |
5 | * University of Szeged, Hungary | | 5 | * University of Szeged, Hungary |
6 | * Copyright (c) 2010 Adam Hoka <ahoka@NetBSD.org> | | 6 | * Copyright (c) 2010 Adam Hoka <ahoka@NetBSD.org> |
7 | * All rights reserved. | | 7 | * All rights reserved. |
8 | * | | 8 | * |
9 | * This code is derived from software contributed to The NetBSD Foundation | | 9 | * This code is derived from software contributed to The NetBSD Foundation |
10 | * by the Department of Software Engineering, University of Szeged, Hungary | | 10 | * by the Department of Software Engineering, University of Szeged, Hungary |
11 | * | | 11 | * |
12 | * Redistribution and use in source and binary forms, with or without | | 12 | * Redistribution and use in source and binary forms, with or without |
13 | * modification, are permitted provided that the following conditions | | 13 | * modification, are permitted provided that the following conditions |
14 | * are met: | | 14 | * are met: |
15 | * 1. Redistributions of source code must retain the above copyright | | 15 | * 1. Redistributions of source code must retain the above copyright |
16 | * notice, this list of conditions and the following disclaimer. | | 16 | * notice, this list of conditions and the following disclaimer. |
17 | * 2. Redistributions in binary form must reproduce the above copyright | | 17 | * 2. Redistributions in binary form must reproduce the above copyright |
18 | * notice, this list of conditions and the following disclaimer in the | | 18 | * notice, this list of conditions and the following disclaimer in the |
19 | * documentation and/or other materials provided with the distribution. | | 19 | * documentation and/or other materials provided with the distribution. |
20 | * | | 20 | * |
21 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | | 21 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
22 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | | 22 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
23 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | | 23 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
24 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | | 24 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
25 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | | 25 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
26 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | | 26 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
27 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | | 27 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
28 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | | 28 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
29 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 29 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | 30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
31 | * SUCH DAMAGE. | | 31 | * SUCH DAMAGE. |
32 | */ | | 32 | */ |
33 | | | 33 | |
34 | /* Device driver for the NAND controller found in Texas Instruments OMAP2 | | 34 | /* Device driver for the NAND controller found in Texas Instruments OMAP2 |
35 | * and later SOCs. | | 35 | * and later SOCs. |
36 | */ | | 36 | */ |
37 | | | 37 | |
38 | #include <sys/cdefs.h> | | 38 | #include <sys/cdefs.h> |
39 | __KERNEL_RCSID(0, "$NetBSD: omap2_nand.c,v 1.5 2012/10/27 17:17:40 chs Exp $"); | | 39 | __KERNEL_RCSID(0, "$NetBSD: omap2_nand.c,v 1.6 2016/10/04 15:32:02 kiyohara Exp $"); |
40 | | | 40 | |
41 | #include "opt_omap.h" | | 41 | #include "opt_omap.h" |
42 | #include "opt_flash.h" | | 42 | #include "opt_flash.h" |
43 | | | 43 | |
44 | /* TODO move to opt_* */ | | 44 | /* TODO move to opt_* */ |
45 | #undef OMAP2_NAND_HARDWARE_ECC | | 45 | #undef OMAP2_NAND_HARDWARE_ECC |
46 | | | 46 | |
47 | #include <sys/param.h> | | 47 | #include <sys/param.h> |
48 | #include <sys/systm.h> | | 48 | #include <sys/systm.h> |
49 | #include <sys/cdefs.h> | | 49 | #include <sys/cdefs.h> |
50 | #include <sys/device.h> | | 50 | #include <sys/device.h> |
51 | | | 51 | |
52 | #include <sys/bus.h> | | 52 | #include <sys/bus.h> |
53 | | | 53 | |
54 | #include <arch/arm/omap/omap2_gpmcvar.h> | | 54 | #include <arch/arm/omap/omap2_gpmcvar.h> |
55 | #include <arch/arm/omap/omap2_gpmcreg.h> | | 55 | #include <arch/arm/omap/omap2_gpmcreg.h> |
56 | | | 56 | |
57 | #include <dev/nand/nand.h> | | 57 | #include <dev/nand/nand.h> |
58 | #include <dev/nand/onfi.h> | | 58 | #include <dev/nand/onfi.h> |
59 | | | 59 | |
60 | /* GPMC_STATUS */ | | 60 | /* GPMC_STATUS */ |
61 | #define WAIT0 __BIT(8) /* active low */ | | 61 | #define WAIT0 __BIT(8) /* active low */ |
62 | | | 62 | |
63 | /* GPMC_ECC_CONTROL */ | | 63 | /* GPMC_ECC_CONTROL */ |
64 | #define ECCCLEAR __BIT(8) | | 64 | #define ECCCLEAR __BIT(8) |
65 | #define ECCPOINTER __BITS(3,0) | | 65 | #define ECCPOINTER __BITS(3,0) |
66 | | | 66 | |
67 | /* GPMC_ECC_CONFIG */ | | 67 | /* GPMC_ECC_CONFIG */ |
68 | #define ECCALGORITHM __BIT(16) | | 68 | #define ECCALGORITHM __BIT(16) |
69 | #define ECCCS __BITS(3,1) | | 69 | #define ECCCS __BITS(3,1) |
70 | #define ECC16B __BIT(7) | | 70 | #define ECC16B __BIT(7) |
71 | #define ECCENABLE __BIT(0) | | 71 | #define ECCENABLE __BIT(0) |
72 | /* GPMC_ECC_SIZE_CONFIG */ | | 72 | /* GPMC_ECC_SIZE_CONFIG */ |
73 | #define ECCSIZE1 __BITS(29,22) | | 73 | #define ECCSIZE1 __BITS(29,22) |
74 | | | 74 | |
75 | /* GPMC_CONFIG1_i */ | | 75 | /* GPMC_CONFIG1_i */ |
76 | #define DEVICETYPE __BITS(11,10) | | 76 | #define DEVICETYPE __BITS(11,10) |
77 | #define DEVICESIZE __BITS(13,12) | | 77 | #define DEVICESIZE __BITS(13,12) |
78 | | | 78 | |
79 | #define MASKEDINT(mask, integer) ((integer) << (ffs(mask) - 1) & mask) | | 79 | #define MASKEDINT(mask, integer) ((integer) << (ffs(mask) - 1) & mask) |
80 | | | 80 | |
81 | /* NAND status register */ | | 81 | /* NAND status register */ |
82 | #define NAND_WP_BIT __BIT(4) | | 82 | #define NAND_WP_BIT __BIT(4) |
83 | | | 83 | |
84 | static int omap2_nand_match(device_t, cfdata_t, void *); | | 84 | static int omap2_nand_match(device_t, cfdata_t, void *); |
85 | static void omap2_nand_attach(device_t, device_t, void *); | | 85 | static void omap2_nand_attach(device_t, device_t, void *); |
86 | static int omap2_nand_detach(device_t, int); | | 86 | static int omap2_nand_detach(device_t, int); |
87 | | | 87 | |
88 | void omap2_nand_command(device_t self, uint8_t command); | | 88 | void omap2_nand_command(device_t self, uint8_t command); |
89 | void omap2_nand_address(device_t self, uint8_t address); | | 89 | void omap2_nand_address(device_t self, uint8_t address); |
90 | void omap2_nand_busy(device_t self); | | 90 | void omap2_nand_busy(device_t self); |
91 | void omap2_nand_read_1(device_t self, uint8_t *data); | | 91 | void omap2_nand_read_1(device_t self, uint8_t *data); |
92 | void omap2_nand_write_1(device_t self, uint8_t data); | | 92 | void omap2_nand_write_1(device_t self, uint8_t data); |
93 | void omap2_nand_read_2(device_t self, uint16_t *data); | | 93 | void omap2_nand_read_2(device_t self, uint16_t *data); |
94 | void omap2_nand_write_2(device_t self, uint16_t data); | | 94 | void omap2_nand_write_2(device_t self, uint16_t data); |
95 | bool omap2_nand_isbusy(device_t self); | | 95 | bool omap2_nand_isbusy(device_t self); |
96 | void omap2_nand_read_buf_1(device_t self, void *buf, size_t len); | | 96 | void omap2_nand_read_buf_1(device_t self, void *buf, size_t len); |
97 | void omap2_nand_read_buf_2(device_t self, void *buf, size_t len); | | 97 | void omap2_nand_read_buf_2(device_t self, void *buf, size_t len); |
98 | void omap2_nand_write_buf_1(device_t self, const void *buf, size_t len); | | 98 | void omap2_nand_write_buf_1(device_t self, const void *buf, size_t len); |
99 | void omap2_nand_write_buf_2(device_t self, const void *buf, size_t len); | | 99 | void omap2_nand_write_buf_2(device_t self, const void *buf, size_t len); |
100 | | | 100 | |
101 | int omap2_nand_ecc_init(device_t self); | | 101 | int omap2_nand_ecc_init(device_t self); |
102 | int omap2_nand_ecc_prepare(device_t self, int mode); | | 102 | int omap2_nand_ecc_prepare(device_t self, int mode); |
103 | int omap2_nand_ecc_compute(device_t self, const uint8_t *data, uint8_t *ecc); | | 103 | int omap2_nand_ecc_compute(device_t self, const uint8_t *data, uint8_t *ecc); |
104 | int omap2_nand_ecc_correct(device_t self, uint8_t *data, const uint8_t *oldecc, | | 104 | int omap2_nand_ecc_correct(device_t self, uint8_t *data, const uint8_t *oldecc, |
105 | const uint8_t *calcecc); | | 105 | const uint8_t *calcecc); |
106 | | | 106 | |
107 | struct omap2_nand_softc { | | 107 | struct omap2_nand_softc { |
108 | device_t sc_dev; | | 108 | device_t sc_dev; |
109 | device_t sc_nanddev; | | 109 | device_t sc_nanddev; |
110 | struct gpmc_softc *sc_gpmcsc; | | 110 | struct gpmc_softc *sc_gpmcsc; |
111 | | | 111 | |
112 | int sc_cs; | | 112 | int sc_cs; |
113 | int sc_buswidth; /* 0: 8bit, 1: 16bit */ | | 113 | int sc_buswidth; /* 0: 8bit, 1: 16bit */ |
114 | | | 114 | |
115 | struct nand_interface sc_nand_if; | | 115 | struct nand_interface sc_nand_if; |
116 | | | 116 | |
117 | bus_space_handle_t sc_ioh; | | 117 | bus_space_handle_t sc_ioh; |
118 | bus_space_tag_t sc_iot; | | 118 | bus_space_tag_t sc_iot; |
119 | | | 119 | |
120 | bus_size_t sc_cmd_reg; | | 120 | bus_size_t sc_cmd_reg; |
121 | bus_size_t sc_addr_reg; | | 121 | bus_size_t sc_addr_reg; |
122 | bus_size_t sc_data_reg; | | 122 | bus_size_t sc_data_reg; |
123 | }; | | 123 | }; |
124 | | | 124 | |
125 | CFATTACH_DECL_NEW(omapnand, sizeof(struct omap2_nand_softc), omap2_nand_match, | | 125 | CFATTACH_DECL_NEW(omapnand, sizeof(struct omap2_nand_softc), omap2_nand_match, |
126 | omap2_nand_attach, omap2_nand_detach, NULL); | | 126 | omap2_nand_attach, omap2_nand_detach, NULL); |
127 | | | 127 | |
128 | void | | 128 | void |
129 | omap2_nand_command(device_t self, uint8_t command) | | 129 | omap2_nand_command(device_t self, uint8_t command) |
130 | { | | 130 | { |
131 | struct omap2_nand_softc *sc = device_private(self); | | 131 | struct omap2_nand_softc *sc = device_private(self); |
132 | | | 132 | |
133 | bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_cmd_reg, command); | | 133 | bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_cmd_reg, command); |
134 | }; | | 134 | }; |
135 | | | 135 | |
136 | void | | 136 | void |
137 | omap2_nand_address(device_t self, uint8_t address) | | 137 | omap2_nand_address(device_t self, uint8_t address) |
138 | { | | 138 | { |
139 | struct omap2_nand_softc *sc = device_private(self); | | 139 | struct omap2_nand_softc *sc = device_private(self); |
140 | | | 140 | |
141 | bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_addr_reg, address); | | 141 | bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_addr_reg, address); |
142 | }; | | 142 | }; |
143 | | | 143 | |
144 | bool | | 144 | bool |
145 | omap2_nand_isbusy(device_t self) | | 145 | omap2_nand_isbusy(device_t self) |
146 | { | | 146 | { |
147 | struct omap2_nand_softc *sc = device_private(self); | | 147 | struct omap2_nand_softc *sc = device_private(self); |
148 | uint8_t status; | | 148 | uint8_t status; |
149 | | | 149 | |
150 | DELAY(1); /* just to be sure we are not early */ | | 150 | DELAY(1); /* just to be sure we are not early */ |
151 | | | 151 | |
152 | bus_space_write_1(sc->sc_iot, sc->sc_ioh, | | 152 | bus_space_write_1(sc->sc_iot, sc->sc_ioh, |
153 | sc->sc_cmd_reg, ONFI_READ_STATUS); | | 153 | sc->sc_cmd_reg, ONFI_READ_STATUS); |
154 | | | 154 | |
155 | DELAY(1); | | 155 | DELAY(1); |
156 | | | 156 | |
157 | status = bus_space_read_1(sc->sc_iot, | | 157 | status = bus_space_read_1(sc->sc_iot, |
158 | sc->sc_ioh, sc->sc_data_reg); | | 158 | sc->sc_ioh, sc->sc_data_reg); |
159 | | | 159 | |
160 | return !(status & ONFI_STATUS_RDY); | | 160 | return !(status & ONFI_STATUS_RDY); |
161 | }; | | 161 | }; |
162 | | | 162 | |
163 | static int | | 163 | static int |
164 | omap2_nand_match(device_t parent, cfdata_t match, void *aux) | | 164 | omap2_nand_match(device_t parent, cfdata_t match, void *aux) |
165 | { | | 165 | { |
166 | struct gpmc_attach_args *gpmc = aux; | | 166 | struct gpmc_attach_args *gpmc = aux; |
167 | bus_space_tag_t iot; | | 167 | bus_space_tag_t iot; |
168 | bus_space_handle_t ioh; | | 168 | bus_space_handle_t ioh; |
169 | bus_size_t cs_offset; | | 169 | bus_size_t cs_offset; |
170 | uint32_t result; | | 170 | uint32_t result; |
171 | int ret = 0; | | 171 | int ret = 0; |
172 | | | 172 | |
173 | iot = gpmc->gpmc_iot; | | 173 | iot = gpmc->gpmc_iot; |
174 | | | 174 | |
175 | cs_offset = GPMC_CS_CONFIG_BASE(gpmc->gpmc_cs); | | 175 | cs_offset = GPMC_CS_CONFIG_BASE(gpmc->gpmc_cs); |
176 | | | 176 | |
177 | /* map i/o space */ | | 177 | /* map i/o space */ |
178 | if (bus_space_map(iot, cs_offset, GPMC_CS_SIZE, 0, &ioh) != 0) { | | 178 | if (bus_space_map(iot, cs_offset, GPMC_CS_SIZE, 0, &ioh) != 0) { |
179 | aprint_error("omap2_nand_match: can't map i/o space"); | | 179 | aprint_error("omap2_nand_match: can't map i/o space"); |
180 | return 1; | | 180 | return 1; |
181 | } | | 181 | } |
182 | | | 182 | |
183 | /* read GPMC_CONFIG1_i */ | | 183 | /* read GPMC_CONFIG1_i */ |
184 | result = bus_space_read_4(iot, ioh, GPMC_CONFIG1_i); | | 184 | result = bus_space_read_4(iot, ioh, GPMC_CONFIG1_i); |
185 | | | 185 | |
186 | /* check if memory device is NAND type */ | | 186 | /* check if memory device is NAND type */ |
187 | if ((result & DEVICETYPE) == MASKEDINT(DEVICETYPE, 0x02)) { | | 187 | if ((result & DEVICETYPE) == MASKEDINT(DEVICETYPE, 0x02)) { |
188 | /* we got NAND, report positive match */ | | 188 | /* we got NAND, report positive match */ |
189 | ret = 1; | | 189 | ret = 1; |
190 | } | | 190 | } |
191 | | | 191 | |
192 | bus_space_unmap(iot, ioh, GPMC_CS_SIZE); | | 192 | bus_space_unmap(iot, ioh, GPMC_CS_SIZE); |
193 | | | 193 | |
194 | return ret; | | 194 | return ret; |
195 | } | | 195 | } |
196 | | | 196 | |
197 | static void | | 197 | static void |
198 | omap2_nand_attach(device_t parent, device_t self, void *aux) | | 198 | omap2_nand_attach(device_t parent, device_t self, void *aux) |
199 | { | | 199 | { |
200 | struct omap2_nand_softc *sc = device_private(self); | | 200 | struct omap2_nand_softc *sc = device_private(self); |
201 | sc->sc_gpmcsc = device_private(parent); | | 201 | sc->sc_gpmcsc = device_private(parent); |
202 | struct gpmc_attach_args *gpmc = aux; | | 202 | struct gpmc_attach_args *gpmc = aux; |
203 | bus_size_t cs_offset; | | 203 | bus_size_t cs_offset; |
204 | uint32_t val; | | 204 | uint32_t val; |
205 | | | 205 | |
206 | aprint_normal("\n"); | | 206 | aprint_normal("\n"); |
207 | | | 207 | |
208 | sc->sc_iot = gpmc->gpmc_iot; | | 208 | sc->sc_iot = gpmc->gpmc_iot; |
209 | sc->sc_dev = self; | | 209 | sc->sc_dev = self; |
210 | sc->sc_cs = gpmc->gpmc_cs; | | 210 | sc->sc_cs = gpmc->gpmc_cs; |
211 | | | 211 | |
212 | // cs_offset = GPMC_BASE + GPMC_CONFIG1_0 + sc->sc_cs * GPMC_CS_SIZE; | | | |
213 | cs_offset = GPMC_CS_CONFIG_BASE(sc->sc_cs); | | 212 | cs_offset = GPMC_CS_CONFIG_BASE(sc->sc_cs); |
214 | | | 213 | |
215 | /* map i/o space */ | | 214 | /* map i/o space */ |
216 | if (bus_space_map(sc->sc_iot, cs_offset, GPMC_CS_SIZE, 0, | | 215 | if (bus_space_map(sc->sc_iot, cs_offset, GPMC_CS_SIZE, 0, |
217 | &sc->sc_ioh) != 0) { | | 216 | &sc->sc_ioh) != 0) { |
218 | aprint_error(": omap2_nand_attach: can't map i/o space"); | | 217 | aprint_error(": omap2_nand_attach: can't map i/o space"); |
219 | return; | | 218 | return; |
220 | } | | 219 | } |
221 | | | 220 | |
222 | sc->sc_cmd_reg = GPMC_NAND_COMMAND_0 - GPMC_CONFIG1_0; | | 221 | sc->sc_cmd_reg = GPMC_NAND_COMMAND_0 - GPMC_CONFIG1_0; |
223 | sc->sc_addr_reg = GPMC_NAND_ADDRESS_0 - GPMC_CONFIG1_0; | | 222 | sc->sc_addr_reg = GPMC_NAND_ADDRESS_0 - GPMC_CONFIG1_0; |
224 | sc->sc_data_reg = GPMC_NAND_DATA_0 - GPMC_CONFIG1_0; | | 223 | sc->sc_data_reg = GPMC_NAND_DATA_0 - GPMC_CONFIG1_0; |
225 | | | 224 | |
226 | /* turn off write protection if enabled */ | | 225 | /* turn off write protection if enabled */ |
227 | val = gpmc_register_read(sc->sc_gpmcsc, GPMC_CONFIG); | | 226 | val = gpmc_register_read(sc->sc_gpmcsc, GPMC_CONFIG); |
228 | val |= NAND_WP_BIT; | | 227 | val |= NAND_WP_BIT; |
229 | gpmc_register_write(sc->sc_gpmcsc, GPMC_CONFIG, val); | | 228 | gpmc_register_write(sc->sc_gpmcsc, GPMC_CONFIG, val); |
230 | | | 229 | |
231 | /* | | 230 | /* |
232 | * do the reset dance for NAND | | 231 | * do the reset dance for NAND |
233 | */ | | 232 | */ |
234 | bus_space_write_1(sc->sc_iot, sc->sc_ioh, | | 233 | bus_space_write_1(sc->sc_iot, sc->sc_ioh, |
235 | sc->sc_cmd_reg, ONFI_RESET); | | 234 | sc->sc_cmd_reg, ONFI_RESET); |
236 | | | 235 | |
237 | omap2_nand_busy(self); | | 236 | omap2_nand_busy(self); |
238 | | | 237 | |
239 | /* read GPMC_CONFIG1_i to get buswidth */ | | 238 | /* read GPMC_CONFIG1_i to get buswidth */ |
240 | val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPMC_CONFIG1_i); | | 239 | val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPMC_CONFIG1_i); |
241 | | | 240 | |
242 | if ((val & DEVICESIZE) == MASKEDINT(DEVICESIZE, 0x01)) { | | 241 | if ((val & DEVICESIZE) == MASKEDINT(DEVICESIZE, 0x01)) { |
243 | /* 16bit */ | | 242 | /* 16bit */ |
244 | sc->sc_buswidth = 1; | | 243 | sc->sc_buswidth = 1; |
245 | } else if ((val & DEVICESIZE) == MASKEDINT(DEVICESIZE, 0x00)) { | | 244 | } else if ((val & DEVICESIZE) == MASKEDINT(DEVICESIZE, 0x00)) { |
246 | /* 8bit */ | | 245 | /* 8bit */ |
247 | sc->sc_buswidth = 0; | | 246 | sc->sc_buswidth = 0; |
248 | } else { | | 247 | } else { |
249 | panic("invalid buswidth reported by config1"); | | 248 | panic("invalid buswidth reported by config1"); |
250 | } | | 249 | } |
251 | | | 250 | |
252 | nand_init_interface(&sc->sc_nand_if); | | 251 | nand_init_interface(&sc->sc_nand_if); |
253 | | | 252 | |
254 | sc->sc_nand_if.command = &omap2_nand_command; | | 253 | sc->sc_nand_if.command = &omap2_nand_command; |
255 | sc->sc_nand_if.address = &omap2_nand_address; | | 254 | sc->sc_nand_if.address = &omap2_nand_address; |
256 | sc->sc_nand_if.read_buf_1 = &omap2_nand_read_buf_1; | | 255 | sc->sc_nand_if.read_buf_1 = &omap2_nand_read_buf_1; |
257 | sc->sc_nand_if.read_buf_2 = &omap2_nand_read_buf_2; | | 256 | sc->sc_nand_if.read_buf_2 = &omap2_nand_read_buf_2; |
258 | sc->sc_nand_if.read_1 = &omap2_nand_read_1; | | 257 | sc->sc_nand_if.read_1 = &omap2_nand_read_1; |
259 | sc->sc_nand_if.read_2 = &omap2_nand_read_2; | | 258 | sc->sc_nand_if.read_2 = &omap2_nand_read_2; |
260 | sc->sc_nand_if.write_buf_1 = &omap2_nand_write_buf_1; | | 259 | sc->sc_nand_if.write_buf_1 = &omap2_nand_write_buf_1; |
261 | sc->sc_nand_if.write_buf_2 = &omap2_nand_write_buf_2; | | 260 | sc->sc_nand_if.write_buf_2 = &omap2_nand_write_buf_2; |
262 | sc->sc_nand_if.write_1 = &omap2_nand_write_1; | | 261 | sc->sc_nand_if.write_1 = &omap2_nand_write_1; |
263 | sc->sc_nand_if.write_2 = &omap2_nand_write_2; | | 262 | sc->sc_nand_if.write_2 = &omap2_nand_write_2; |
264 | sc->sc_nand_if.busy = &omap2_nand_busy; | | 263 | sc->sc_nand_if.busy = &omap2_nand_busy; |
265 | | | 264 | |
266 | #ifdef OMAP2_NAND_HARDWARE_ECC | | 265 | #ifdef OMAP2_NAND_HARDWARE_ECC |
267 | omap2_nand_ecc_init(self); | | 266 | omap2_nand_ecc_init(self); |
268 | sc->sc_nand_if.ecc_compute = &omap2_nand_ecc_compute; | | 267 | sc->sc_nand_if.ecc_compute = &omap2_nand_ecc_compute; |
269 | sc->sc_nand_if.ecc_correct = &omap2_nand_ecc_correct; | | 268 | sc->sc_nand_if.ecc_correct = &omap2_nand_ecc_correct; |
270 | sc->sc_nand_if.ecc_prepare = &omap2_nand_ecc_prepare; | | 269 | sc->sc_nand_if.ecc_prepare = &omap2_nand_ecc_prepare; |
271 | sc->sc_nand_if.ecc.necc_code_size = 3; | | 270 | sc->sc_nand_if.ecc.necc_code_size = 3; |
272 | sc->sc_nand_if.ecc.necc_block_size = 512; | | 271 | sc->sc_nand_if.ecc.necc_block_size = 512; |
273 | sc->sc_nand_if.ecc.necc_type = NAND_ECC_TYPE_HW; | | 272 | sc->sc_nand_if.ecc.necc_type = NAND_ECC_TYPE_HW; |
274 | #else | | 273 | #else |
275 | sc->sc_nand_if.ecc.necc_code_size = 3; | | 274 | sc->sc_nand_if.ecc.necc_code_size = 3; |
276 | sc->sc_nand_if.ecc.necc_block_size = 256; | | 275 | sc->sc_nand_if.ecc.necc_block_size = 256; |
277 | #endif /* OMAP2_NAND_HARDWARE_ECC */ | | 276 | #endif /* OMAP2_NAND_HARDWARE_ECC */ |
278 | | | 277 | |
279 | if (!pmf_device_register1(sc->sc_dev, NULL, NULL, NULL)) | | 278 | if (!pmf_device_register1(sc->sc_dev, NULL, NULL, NULL)) |
280 | aprint_error_dev(sc->sc_dev, | | 279 | aprint_error_dev(sc->sc_dev, |
281 | "couldn't establish power handler\n"); | | 280 | "couldn't establish power handler\n"); |
282 | | | 281 | |
283 | sc->sc_nanddev = nand_attach_mi(&sc->sc_nand_if, sc->sc_dev); | | 282 | sc->sc_nanddev = nand_attach_mi(&sc->sc_nand_if, sc->sc_dev); |
284 | } | | 283 | } |
285 | | | 284 | |
286 | static int | | 285 | static int |
287 | omap2_nand_detach(device_t device, int flags) | | 286 | omap2_nand_detach(device_t device, int flags) |
288 | { | | 287 | { |
289 | struct omap2_nand_softc *sc = device_private(device); | | 288 | struct omap2_nand_softc *sc = device_private(device); |
290 | int ret = 0; | | 289 | int ret = 0; |
291 | | | 290 | |
292 | bus_space_unmap(sc->sc_iot, sc->sc_ioh, GPMC_CS_SIZE); | | 291 | bus_space_unmap(sc->sc_iot, sc->sc_ioh, GPMC_CS_SIZE); |
293 | | | 292 | |
294 | pmf_device_deregister(sc->sc_dev); | | 293 | pmf_device_deregister(sc->sc_dev); |
295 | | | 294 | |
296 | if (sc->sc_nanddev != NULL) | | 295 | if (sc->sc_nanddev != NULL) |
297 | ret = config_detach(sc->sc_nanddev, flags); | | 296 | ret = config_detach(sc->sc_nanddev, flags); |
298 | | | 297 | |
299 | return ret; | | 298 | return ret; |
300 | } | | 299 | } |
301 | | | 300 | |
302 | void | | 301 | void |
303 | omap2_nand_busy(device_t self) | | 302 | omap2_nand_busy(device_t self) |
304 | { | | 303 | { |
305 | struct omap2_nand_softc *sc = device_private(self); | | 304 | struct omap2_nand_softc *sc = device_private(self); |
306 | | | 305 | |
307 | while (!(gpmc_register_read(sc->sc_gpmcsc, GPMC_STATUS) & WAIT0)) { | | 306 | while (!(gpmc_register_read(sc->sc_gpmcsc, GPMC_STATUS) & WAIT0)) { |
308 | DELAY(1); | | 307 | DELAY(1); |
309 | } | | 308 | } |
310 | } | | 309 | } |
311 | | | 310 | |
312 | void | | 311 | void |
313 | omap2_nand_read_1(device_t self, uint8_t *data) | | 312 | omap2_nand_read_1(device_t self, uint8_t *data) |
314 | { | | 313 | { |
315 | struct omap2_nand_softc *sc = device_private(self); | | 314 | struct omap2_nand_softc *sc = device_private(self); |
316 | | | 315 | |
317 | *data = bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg); | | 316 | *data = bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg); |
318 | } | | 317 | } |
319 | | | 318 | |
320 | void | | 319 | void |
321 | omap2_nand_write_1(device_t self, uint8_t data) | | 320 | omap2_nand_write_1(device_t self, uint8_t data) |
322 | { | | 321 | { |
323 | struct omap2_nand_softc *sc = device_private(self); | | 322 | struct omap2_nand_softc *sc = device_private(self); |
324 | | | 323 | |
325 | bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg, data); | | 324 | bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg, data); |
326 | } | | 325 | } |
327 | | | 326 | |
328 | void | | 327 | void |
329 | omap2_nand_read_2(device_t self, uint16_t *data) | | 328 | omap2_nand_read_2(device_t self, uint16_t *data) |
330 | { | | 329 | { |
331 | struct omap2_nand_softc *sc = device_private(self); | | 330 | struct omap2_nand_softc *sc = device_private(self); |
332 | | | 331 | |
333 | *data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg); | | 332 | *data = bus_space_read_2(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg); |
334 | } | | 333 | } |
335 | | | 334 | |
336 | void | | 335 | void |
337 | omap2_nand_write_2(device_t self, uint16_t data) | | 336 | omap2_nand_write_2(device_t self, uint16_t data) |
338 | { | | 337 | { |
339 | struct omap2_nand_softc *sc = device_private(self); | | 338 | struct omap2_nand_softc *sc = device_private(self); |
340 | | | 339 | |
341 | bus_space_write_2(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg, data); | | 340 | bus_space_write_2(sc->sc_iot, sc->sc_ioh, sc->sc_data_reg, data); |
342 | } | | 341 | } |
343 | | | 342 | |
344 | void | | 343 | void |
345 | omap2_nand_read_buf_1(device_t self, void *buf, size_t len) | | 344 | omap2_nand_read_buf_1(device_t self, void *buf, size_t len) |
346 | { | | 345 | { |
347 | struct omap2_nand_softc *sc = device_private(self); | | 346 | struct omap2_nand_softc *sc = device_private(self); |
348 | | | 347 | |
349 | KASSERT(buf != NULL); | | 348 | KASSERT(buf != NULL); |
350 | KASSERT(len >= 1); | | 349 | KASSERT(len >= 1); |
351 | | | 350 | |
352 | bus_space_read_multi_1(sc->sc_iot, sc->sc_ioh, | | 351 | bus_space_read_multi_1(sc->sc_iot, sc->sc_ioh, |
353 | sc->sc_data_reg, buf, len); | | 352 | sc->sc_data_reg, buf, len); |
354 | } | | 353 | } |
355 | | | 354 | |
356 | void | | 355 | void |
357 | omap2_nand_read_buf_2(device_t self, void *buf, size_t len) | | 356 | omap2_nand_read_buf_2(device_t self, void *buf, size_t len) |
358 | { | | 357 | { |
359 | struct omap2_nand_softc *sc = device_private(self); | | 358 | struct omap2_nand_softc *sc = device_private(self); |
360 | | | 359 | |
361 | KASSERT(buf != NULL); | | 360 | KASSERT(buf != NULL); |
362 | KASSERT(len >= 2); | | 361 | KASSERT(len >= 2); |
363 | KASSERT(!(len & 0x01)); | | 362 | KASSERT(!(len & 0x01)); |
364 | | | 363 | |
365 | bus_space_read_multi_2(sc->sc_iot, sc->sc_ioh, | | 364 | bus_space_read_multi_2(sc->sc_iot, sc->sc_ioh, |
366 | sc->sc_data_reg, buf, len / 2); | | 365 | sc->sc_data_reg, buf, len / 2); |
367 | } | | 366 | } |
368 | | | 367 | |
369 | void | | 368 | void |
370 | omap2_nand_write_buf_1(device_t self, const void *buf, size_t len) | | 369 | omap2_nand_write_buf_1(device_t self, const void *buf, size_t len) |
371 | { | | 370 | { |
372 | struct omap2_nand_softc *sc = device_private(self); | | 371 | struct omap2_nand_softc *sc = device_private(self); |
373 | | | 372 | |
374 | KASSERT(buf != NULL); | | 373 | KASSERT(buf != NULL); |
375 | KASSERT(len >= 1); | | 374 | KASSERT(len >= 1); |
376 | | | 375 | |
377 | bus_space_write_multi_1(sc->sc_iot, sc->sc_ioh, | | 376 | bus_space_write_multi_1(sc->sc_iot, sc->sc_ioh, |
378 | sc->sc_data_reg, buf, len); | | 377 | sc->sc_data_reg, buf, len); |
379 | } | | 378 | } |
380 | | | 379 | |
381 | void | | 380 | void |
382 | omap2_nand_write_buf_2(device_t self, const void *buf, size_t len) | | 381 | omap2_nand_write_buf_2(device_t self, const void *buf, size_t len) |
383 | { | | 382 | { |
384 | struct omap2_nand_softc *sc = device_private(self); | | 383 | struct omap2_nand_softc *sc = device_private(self); |
385 | | | 384 | |
386 | KASSERT(buf != NULL); | | 385 | KASSERT(buf != NULL); |
387 | KASSERT(len >= 2); | | 386 | KASSERT(len >= 2); |
388 | KASSERT(!(len & 0x01)); | | 387 | KASSERT(!(len & 0x01)); |
389 | | | 388 | |
390 | bus_space_write_multi_2(sc->sc_iot, sc->sc_ioh, | | 389 | bus_space_write_multi_2(sc->sc_iot, sc->sc_ioh, |
391 | sc->sc_data_reg, buf, len / 2); | | 390 | sc->sc_data_reg, buf, len / 2); |
392 | } | | 391 | } |
393 | | | 392 | |
394 | static uint32_t | | 393 | static uint32_t |
395 | convert_ecc(const uint8_t *ecc) | | 394 | convert_ecc(const uint8_t *ecc) |
396 | { | | 395 | { |
397 | return ecc[0] | (ecc[1] << 16) | ((ecc[2] & 0xf0) << 20) | | | 396 | return ecc[0] | (ecc[1] << 16) | ((ecc[2] & 0xf0) << 20) | |
398 | ((ecc[2] & 0x0f) << 8); | | 397 | ((ecc[2] & 0x0f) << 8); |
399 | } | | 398 | } |
400 | | | 399 | |
401 | int | | 400 | int |
402 | omap2_nand_ecc_init(device_t self) | | 401 | omap2_nand_ecc_init(device_t self) |
403 | { | | 402 | { |
404 | struct omap2_nand_softc *sc = device_private(self); | | 403 | struct omap2_nand_softc *sc = device_private(self); |
405 | uint32_t val; | | 404 | uint32_t val; |
406 | | | 405 | |
407 | val = gpmc_register_read(sc->sc_gpmcsc, GPMC_ECC_CONTROL); | | 406 | val = gpmc_register_read(sc->sc_gpmcsc, GPMC_ECC_CONTROL); |
408 | /* clear ecc, select ecc register 1 */ | | 407 | /* clear ecc, select ecc register 1 */ |
409 | val &= ~ECCPOINTER; | | 408 | val &= ~ECCPOINTER; |
410 | val |= ECCCLEAR | MASKEDINT(ECCPOINTER, 1); | | 409 | val |= ECCCLEAR | MASKEDINT(ECCPOINTER, 1); |
411 | gpmc_register_write(sc->sc_gpmcsc, GPMC_ECC_CONTROL, val); | | 410 | gpmc_register_write(sc->sc_gpmcsc, GPMC_ECC_CONTROL, val); |
412 | | | 411 | |
413 | /* XXX too many MAGIC */ | | 412 | /* XXX too many MAGIC */ |
414 | /* set ecc size to 512, set all regs to eccsize1*/ | | 413 | /* set ecc size to 512, set all regs to eccsize1*/ |
415 | val = gpmc_register_read(sc->sc_gpmcsc, GPMC_ECC_SIZE_CONFIG); | | 414 | val = gpmc_register_read(sc->sc_gpmcsc, GPMC_ECC_SIZE_CONFIG); |
416 | val &= ~ECCSIZE1; | | 415 | val &= ~ECCSIZE1; |
417 | val |= MASKEDINT(ECCSIZE1, 512) | 0x0f; | | 416 | val |= MASKEDINT(ECCSIZE1, 512) | 0x0f; |
418 | gpmc_register_write(sc->sc_gpmcsc, GPMC_ECC_CONTROL, val); | | 417 | gpmc_register_write(sc->sc_gpmcsc, GPMC_ECC_CONTROL, val); |
419 | | | 418 | |
420 | return 0; | | 419 | return 0; |
421 | } | | 420 | } |
422 | | | 421 | |
423 | int | | 422 | int |
424 | omap2_nand_ecc_compute(device_t self, const uint8_t *data, uint8_t *ecc) | | 423 | omap2_nand_ecc_compute(device_t self, const uint8_t *data, uint8_t *ecc) |
425 | { | | 424 | { |
426 | struct omap2_nand_softc *sc = device_private(self); | | 425 | struct omap2_nand_softc *sc = device_private(self); |
427 | uint32_t val; | | 426 | uint32_t val; |
428 | | | 427 | |
429 | /* read ecc result register */ | | 428 | /* read ecc result register */ |
430 | val = gpmc_register_read(sc->sc_gpmcsc, GPMC_ECC1_RESULT); | | 429 | val = gpmc_register_read(sc->sc_gpmcsc, GPMC_ECC1_RESULT); |
431 | | | 430 | |
432 | ecc[0] = val & 0xff; | | 431 | ecc[0] = val & 0xff; |
433 | ecc[1] = (val >> 16) & 0xff; | | 432 | ecc[1] = (val >> 16) & 0xff; |
434 | ecc[2] = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0); | | 433 | ecc[2] = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0); |
435 | | | 434 | |
436 | /* disable ecc engine */ | | 435 | /* disable ecc engine */ |
437 | val = gpmc_register_read(sc->sc_gpmcsc, GPMC_ECC_CONFIG); | | 436 | val = gpmc_register_read(sc->sc_gpmcsc, GPMC_ECC_CONFIG); |
438 | val &= ~ECCENABLE; | | 437 | val &= ~ECCENABLE; |
439 | gpmc_register_write(sc->sc_gpmcsc, GPMC_ECC_CONFIG, val); | | 438 | gpmc_register_write(sc->sc_gpmcsc, GPMC_ECC_CONFIG, val); |
440 | | | 439 | |
441 | return 0; | | 440 | return 0; |
442 | } | | 441 | } |
443 | | | 442 | |
444 | int | | 443 | int |
445 | omap2_nand_ecc_prepare(device_t self, int mode) | | 444 | omap2_nand_ecc_prepare(device_t self, int mode) |
446 | { | | 445 | { |
447 | struct omap2_nand_softc *sc = device_private(self); | | 446 | struct omap2_nand_softc *sc = device_private(self); |
448 | uint32_t val; | | 447 | uint32_t val; |
449 | | | 448 | |
450 | /* same for read/write */ | | 449 | /* same for read/write */ |
451 | switch (mode) { | | 450 | switch (mode) { |
452 | case NAND_ECC_READ: | | 451 | case NAND_ECC_READ: |
453 | case NAND_ECC_WRITE: | | 452 | case NAND_ECC_WRITE: |
454 | val = gpmc_register_read(sc->sc_gpmcsc, GPMC_ECC_CONTROL); | | 453 | val = gpmc_register_read(sc->sc_gpmcsc, GPMC_ECC_CONTROL); |
455 | /* clear ecc, select ecc register 1 */ | | 454 | /* clear ecc, select ecc register 1 */ |
456 | val &= ~ECCPOINTER; | | 455 | val &= ~ECCPOINTER; |
457 | val |= ECCCLEAR | MASKEDINT(ECCPOINTER, 1); | | 456 | val |= ECCCLEAR | MASKEDINT(ECCPOINTER, 1); |
458 | gpmc_register_write(sc->sc_gpmcsc, GPMC_ECC_CONTROL, val); | | 457 | gpmc_register_write(sc->sc_gpmcsc, GPMC_ECC_CONTROL, val); |
459 | | | 458 | |
460 | val = gpmc_register_read(sc->sc_gpmcsc, GPMC_ECC_CONFIG); | | 459 | val = gpmc_register_read(sc->sc_gpmcsc, GPMC_ECC_CONFIG); |
461 | val &= ~ECCCS; | | 460 | val &= ~ECCCS; |
462 | val |= ECCENABLE | MASKEDINT(ECCCS, sc->sc_cs); | | 461 | val |= ECCENABLE | MASKEDINT(ECCCS, sc->sc_cs); |
463 | if (sc->sc_buswidth == 1) | | 462 | if (sc->sc_buswidth == 1) |
464 | val |= ECC16B; | | 463 | val |= ECC16B; |
465 | else | | 464 | else |
466 | val &= ~ECC16B; | | 465 | val &= ~ECC16B; |
467 | gpmc_register_write(sc->sc_gpmcsc, GPMC_ECC_CONFIG, val); | | 466 | gpmc_register_write(sc->sc_gpmcsc, GPMC_ECC_CONFIG, val); |
468 | | | 467 | |
469 | break; | | 468 | break; |
470 | default: | | 469 | default: |
471 | aprint_error_dev(self, "invalid i/o mode for ecc prepare\n"); | | 470 | aprint_error_dev(self, "invalid i/o mode for ecc prepare\n"); |
472 | return -1; | | 471 | return -1; |
473 | } | | 472 | } |
474 | | | 473 | |
475 | return 0; | | 474 | return 0; |
476 | } | | 475 | } |
477 | | | 476 | |
478 | int | | 477 | int |
479 | omap2_nand_ecc_correct(device_t self, uint8_t *data, const uint8_t *oldecc, | | 478 | omap2_nand_ecc_correct(device_t self, uint8_t *data, const uint8_t *oldecc, |
480 | const uint8_t *calcecc) | | 479 | const uint8_t *calcecc) |
481 | { | | 480 | { |
482 | uint32_t oecc, cecc, xor; | | 481 | uint32_t oecc, cecc, xor; |
483 | uint16_t parity, offset; | | 482 | uint16_t parity, offset; |
484 | uint8_t bit; | | 483 | uint8_t bit; |
485 | | | 484 | |
486 | oecc = convert_ecc(oldecc); | | 485 | oecc = convert_ecc(oldecc); |
487 | cecc = convert_ecc(calcecc); | | 486 | cecc = convert_ecc(calcecc); |
488 | | | 487 | |
489 | /* get the difference */ | | 488 | /* get the difference */ |
490 | xor = oecc ^ cecc; | | 489 | xor = oecc ^ cecc; |
491 | | | 490 | |
492 | /* the data was correct if all bits are zero */ | | 491 | /* the data was correct if all bits are zero */ |
493 | if (xor == 0x00) | | 492 | if (xor == 0x00) |
494 | return NAND_ECC_OK; | | 493 | return NAND_ECC_OK; |
495 | | | 494 | |
496 | switch (popcount32(xor)) { | | 495 | switch (popcount32(xor)) { |
497 | case 12: | | 496 | case 12: |
498 | /* single byte error */ | | 497 | /* single byte error */ |
499 | parity = xor >> 16; | | 498 | parity = xor >> 16; |
500 | bit = (parity & 0x07); | | 499 | bit = (parity & 0x07); |
501 | offset = (parity >> 3) & 0x01ff; | | 500 | offset = (parity >> 3) & 0x01ff; |
502 | /* correct bit */ | | 501 | /* correct bit */ |
503 | data[offset] ^= (0x01 << bit); | | 502 | data[offset] ^= (0x01 << bit); |
504 | return NAND_ECC_CORRECTED; | | 503 | return NAND_ECC_CORRECTED; |
505 | case 1: | | 504 | case 1: |
506 | return NAND_ECC_INVALID; | | 505 | return NAND_ECC_INVALID; |
507 | default: | | 506 | default: |
508 | /* erased page! */ | | 507 | /* erased page! */ |
509 | if ((oecc == 0x0fff0fff) && (cecc == 0x00000000)) | | 508 | if ((oecc == 0x0fff0fff) && (cecc == 0x00000000)) |
510 | return NAND_ECC_OK; | | 509 | return NAND_ECC_OK; |
511 | | | 510 | |
512 | return NAND_ECC_TWOBIT; | | 511 | return NAND_ECC_TWOBIT; |
513 | } | | 512 | } |
514 | } | | 513 | } |