xref: /freebsd/sys/dev/cfi/cfi_core.c (revision 54ebdd631db8c0bba2baab0155f603a8b5cf014a)
1 /*-
2  * Copyright (c) 2007, Juniper Networks, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the author nor the names of any co-contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/bus.h>
36 #include <sys/conf.h>
37 #include <sys/kernel.h>
38 #include <sys/malloc.h>
39 #include <sys/module.h>
40 #include <sys/rman.h>
41 #include <sys/sysctl.h>
42 
43 #include <machine/bus.h>
44 
45 #include <dev/cfi/cfi_reg.h>
46 #include <dev/cfi/cfi_var.h>
47 
48 extern struct cdevsw cfi_cdevsw;
49 
50 char cfi_driver_name[] = "cfi";
51 devclass_t cfi_devclass;
52 
53 uint32_t
54 cfi_read(struct cfi_softc *sc, u_int ofs)
55 {
56 	uint32_t val;
57 
58 	ofs &= ~(sc->sc_width - 1);
59 	switch (sc->sc_width) {
60 	case 1:
61 		val = bus_space_read_1(sc->sc_tag, sc->sc_handle, ofs);
62 		break;
63 	case 2:
64 		val = bus_space_read_2(sc->sc_tag, sc->sc_handle, ofs);
65 		break;
66 	case 4:
67 		val = bus_space_read_4(sc->sc_tag, sc->sc_handle, ofs);
68 		break;
69 	default:
70 		val = ~0;
71 		break;
72 	}
73 
74 	return (val);
75 }
76 
77 static void
78 cfi_write(struct cfi_softc *sc, u_int ofs, u_int val)
79 {
80 
81 	ofs &= ~(sc->sc_width - 1);
82 	switch (sc->sc_width) {
83 	case 1:
84 		bus_space_write_1(sc->sc_tag, sc->sc_handle, ofs, val);
85 		break;
86 	case 2:
87 		bus_space_write_2(sc->sc_tag, sc->sc_handle, ofs, val);
88 		break;
89 	case 4:
90 		bus_space_write_4(sc->sc_tag, sc->sc_handle, ofs, val);
91 		break;
92 	}
93 }
94 
95 uint8_t
96 cfi_read_qry(struct cfi_softc *sc, u_int ofs)
97 {
98 	uint8_t val;
99 
100 	cfi_write(sc, CFI_QRY_CMD_ADDR * sc->sc_width, CFI_QRY_CMD_DATA);
101 	val = cfi_read(sc, ofs * sc->sc_width);
102 	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
103 	return (val);
104 }
105 
106 static void
107 cfi_amd_write(struct cfi_softc *sc, u_int ofs, u_int addr, u_int data)
108 {
109 
110 	cfi_write(sc, ofs + AMD_ADDR_START, CFI_AMD_UNLOCK);
111 	cfi_write(sc, ofs + AMD_ADDR_ACK, CFI_AMD_UNLOCK_ACK);
112 	cfi_write(sc, ofs + addr, data);
113 }
114 
115 static char *
116 cfi_fmtsize(uint32_t sz)
117 {
118 	static char buf[8];
119 	static const char *sfx[] = { "", "K", "M", "G" };
120 	int sfxidx;
121 
122 	sfxidx = 0;
123 	while (sfxidx < 3 && sz > 1023) {
124 		sz /= 1024;
125 		sfxidx++;
126 	}
127 
128 	sprintf(buf, "%u%sB", sz, sfx[sfxidx]);
129 	return (buf);
130 }
131 
132 int
133 cfi_probe(device_t dev)
134 {
135 	char desc[80];
136 	struct cfi_softc *sc;
137 	char *vend_str;
138 	int error;
139 	uint16_t iface, vend;
140 
141 	sc = device_get_softc(dev);
142 	sc->sc_dev = dev;
143 
144 	sc->sc_rid = 0;
145 	sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid,
146 	    RF_ACTIVE);
147 	if (sc->sc_res == NULL)
148 		return (ENXIO);
149 
150 	sc->sc_tag = rman_get_bustag(sc->sc_res);
151 	sc->sc_handle = rman_get_bushandle(sc->sc_res);
152 
153 	sc->sc_width = 1;
154 	while (sc->sc_width <= 4) {
155 		if (cfi_read_qry(sc, CFI_QRY_IDENT) == 'Q')
156 			break;
157 		sc->sc_width <<= 1;
158 	}
159 	if (sc->sc_width > 4) {
160 		error = ENXIO;
161 		goto out;
162 	}
163 
164 	/* We got a Q. Check if we also have the R and the Y. */
165 	if (cfi_read_qry(sc, CFI_QRY_IDENT + 1) != 'R' ||
166 	    cfi_read_qry(sc, CFI_QRY_IDENT + 2) != 'Y') {
167 		error = ENXIO;
168 		goto out;
169 	}
170 
171 	/* Get the vendor and command set. */
172 	vend = cfi_read_qry(sc, CFI_QRY_VEND) |
173 	    (cfi_read_qry(sc, CFI_QRY_VEND + 1) << 8);
174 
175 	sc->sc_cmdset = vend;
176 
177 	switch (vend) {
178 	case CFI_VEND_AMD_ECS:
179 	case CFI_VEND_AMD_SCS:
180 		vend_str = "AMD/Fujitsu";
181 		break;
182 	case CFI_VEND_INTEL_ECS:
183 		vend_str = "Intel/Sharp";
184 		break;
185 	case CFI_VEND_INTEL_SCS:
186 		vend_str = "Intel";
187 		break;
188 	case CFI_VEND_MITSUBISHI_ECS:
189 	case CFI_VEND_MITSUBISHI_SCS:
190 		vend_str = "Mitsubishi";
191 		break;
192 	default:
193 		vend_str = "Unknown vendor";
194 		break;
195 	}
196 
197 	/* Get the device size. */
198 	sc->sc_size = 1U << cfi_read_qry(sc, CFI_QRY_SIZE);
199 
200 	/* Sanity-check the I/F */
201 	iface = cfi_read_qry(sc, CFI_QRY_IFACE) |
202 	    (cfi_read_qry(sc, CFI_QRY_IFACE + 1) << 8);
203 
204 	/*
205 	 * Adding 1 to iface will give us a bit-wise "switch"
206 	 * that allows us to test for the interface width by
207 	 * testing a single bit.
208 	 */
209 	iface++;
210 
211 	error = (iface & sc->sc_width) ? 0 : EINVAL;
212 	if (error)
213 		goto out;
214 
215 	snprintf(desc, sizeof(desc), "%s - %s", vend_str,
216 	    cfi_fmtsize(sc->sc_size));
217 	device_set_desc_copy(dev, desc);
218 
219  out:
220 	bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res);
221 	return (error);
222 }
223 
224 int
225 cfi_attach(device_t dev)
226 {
227 	struct cfi_softc *sc;
228 	u_int blksz, blocks;
229 	u_int r, u;
230 
231 	sc = device_get_softc(dev);
232 	sc->sc_dev = dev;
233 
234 	sc->sc_rid = 0;
235 	sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid,
236 	    RF_ACTIVE);
237 	if (sc->sc_res == NULL)
238 		return (ENXIO);
239 
240 	sc->sc_tag = rman_get_bustag(sc->sc_res);
241 	sc->sc_handle = rman_get_bushandle(sc->sc_res);
242 
243 	/* Get time-out values for erase and write. */
244 	sc->sc_write_timeout = 1 << cfi_read_qry(sc, CFI_QRY_TTO_WRITE);
245 	sc->sc_erase_timeout = 1 << cfi_read_qry(sc, CFI_QRY_TTO_ERASE);
246 	sc->sc_write_timeout *= 1 << cfi_read_qry(sc, CFI_QRY_MTO_WRITE);
247 	sc->sc_erase_timeout *= 1 << cfi_read_qry(sc, CFI_QRY_MTO_ERASE);
248 
249 	/* Get erase regions. */
250 	sc->sc_regions = cfi_read_qry(sc, CFI_QRY_NREGIONS);
251 	sc->sc_region = malloc(sc->sc_regions * sizeof(struct cfi_region),
252 	    M_TEMP, M_WAITOK | M_ZERO);
253 	for (r = 0; r < sc->sc_regions; r++) {
254 		blocks = cfi_read_qry(sc, CFI_QRY_REGION(r)) |
255 		    (cfi_read_qry(sc, CFI_QRY_REGION(r) + 1) << 8);
256 		sc->sc_region[r].r_blocks = blocks + 1;
257 
258 		blksz = cfi_read_qry(sc, CFI_QRY_REGION(r) + 2) |
259 		    (cfi_read_qry(sc, CFI_QRY_REGION(r) + 3) << 8);
260 		sc->sc_region[r].r_blksz = (blksz == 0) ? 128 :
261 		    blksz * 256;
262 	}
263 
264 	/* Reset the device to a default state. */
265 	cfi_write(sc, 0, CFI_BCS_CLEAR_STATUS);
266 
267 	if (bootverbose) {
268 		device_printf(dev, "[");
269 		for (r = 0; r < sc->sc_regions; r++) {
270 			printf("%ux%s%s", sc->sc_region[r].r_blocks,
271 			    cfi_fmtsize(sc->sc_region[r].r_blksz),
272 			    (r == sc->sc_regions - 1) ? "]\n" : ",");
273 		}
274 	}
275 
276 	u = device_get_unit(dev);
277 	sc->sc_nod = make_dev(&cfi_cdevsw, u, UID_ROOT, GID_WHEEL, 0600,
278 	    "%s%u", cfi_driver_name, u);
279 	sc->sc_nod->si_drv1 = sc;
280 
281 	return (0);
282 }
283 
284 int
285 cfi_detach(device_t dev)
286 {
287 	struct cfi_softc *sc;
288 
289 	sc = device_get_softc(dev);
290 
291 	destroy_dev(sc->sc_nod);
292 	free(sc->sc_region, M_TEMP);
293 	bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res);
294 	return (0);
295 }
296 
297 static int
298 cfi_wait_ready(struct cfi_softc *sc, u_int timeout)
299 {
300 	int done, error;
301 	uint32_t st0, st;
302 
303 	done = 0;
304 	error = 0;
305 	timeout *= 10;
306 	while (!done && !error && timeout) {
307 		DELAY(100);
308 		timeout--;
309 
310 		switch (sc->sc_cmdset) {
311 		case CFI_VEND_INTEL_ECS:
312 		case CFI_VEND_INTEL_SCS:
313 			st = cfi_read(sc, sc->sc_wrofs);
314 			done = (st & 0x80);
315 			if (done) {
316 				if (st & 0x02)
317 					error = EPERM;
318 				else if (st & 0x10)
319 					error = EIO;
320 				else if (st & 0x20)
321 					error = ENXIO;
322 			}
323 			break;
324 		case CFI_VEND_AMD_SCS:
325 		case CFI_VEND_AMD_ECS:
326 			st0 = cfi_read(sc, sc->sc_wrofs);
327 			st = cfi_read(sc, sc->sc_wrofs);
328 			done = ((st & 0x40) == (st0 & 0x40)) ? 1 : 0;
329 			break;
330 		}
331 	}
332 	if (!done && !error)
333 		error = ETIMEDOUT;
334 	if (error)
335 		printf("\nerror=%d\n", error);
336 	return (error);
337 }
338 
339 int
340 cfi_write_block(struct cfi_softc *sc)
341 {
342 	union {
343 		uint8_t		*x8;
344 		uint16_t	*x16;
345 		uint32_t	*x32;
346 	} ptr;
347 	register_t intr;
348 	int error, i;
349 
350 	/* Erase the block. */
351 	switch (sc->sc_cmdset) {
352 	case CFI_VEND_INTEL_ECS:
353 	case CFI_VEND_INTEL_SCS:
354 		cfi_write(sc, sc->sc_wrofs, CFI_BCS_BLOCK_ERASE);
355 		cfi_write(sc, sc->sc_wrofs, CFI_BCS_CONFIRM);
356 		break;
357 	case CFI_VEND_AMD_SCS:
358 	case CFI_VEND_AMD_ECS:
359 		cfi_amd_write(sc, sc->sc_wrofs, AMD_ADDR_START,
360 		    CFI_AMD_ERASE_SECTOR);
361 		cfi_amd_write(sc, sc->sc_wrofs, 0, CFI_AMD_BLOCK_ERASE);
362 		break;
363 	default:
364 		/* Better safe than sorry... */
365 		return (ENODEV);
366 	}
367 	error = cfi_wait_ready(sc, sc->sc_erase_timeout);
368 	if (error)
369 		goto out;
370 
371 	/* Write the block. */
372 	ptr.x8 = sc->sc_wrbuf;
373 	for (i = 0; i < sc->sc_wrbufsz; i += sc->sc_width) {
374 
375 		/*
376 		 * Make sure the command to start a write and the
377 		 * actual write happens back-to-back without any
378 		 * excessive delays.
379 		 */
380 		intr = intr_disable();
381 
382 		switch (sc->sc_cmdset) {
383 		case CFI_VEND_INTEL_ECS:
384 		case CFI_VEND_INTEL_SCS:
385 			cfi_write(sc, sc->sc_wrofs + i, CFI_BCS_PROGRAM);
386 			break;
387 		case CFI_VEND_AMD_SCS:
388 		case CFI_VEND_AMD_ECS:
389 			cfi_amd_write(sc, 0, AMD_ADDR_START, CFI_AMD_PROGRAM);
390 			break;
391 		}
392 		switch (sc->sc_width) {
393 		case 1:
394 			bus_space_write_1(sc->sc_tag, sc->sc_handle,
395 			    sc->sc_wrofs + i, *(ptr.x8)++);
396 			break;
397 		case 2:
398 			bus_space_write_2(sc->sc_tag, sc->sc_handle,
399 			    sc->sc_wrofs + i, *(ptr.x16)++);
400 			break;
401 		case 4:
402 			bus_space_write_4(sc->sc_tag, sc->sc_handle,
403 			    sc->sc_wrofs + i, *(ptr.x32)++);
404 			break;
405 		}
406 
407 		intr_restore(intr);
408 
409 		error = cfi_wait_ready(sc, sc->sc_write_timeout);
410 		if (error)
411 			goto out;
412 	}
413 
414 	/* error is 0. */
415 
416  out:
417 	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
418 	return (error);
419 }
420