cfi_core.c revision 251118
1184251Smarcel/*-
2184251Smarcel * Copyright (c) 2007, Juniper Networks, Inc.
3184251Smarcel * All rights reserved.
4184251Smarcel *
5184251Smarcel * Redistribution and use in source and binary forms, with or without
6184251Smarcel * modification, are permitted provided that the following conditions
7184251Smarcel * are met:
8184251Smarcel * 1. Redistributions of source code must retain the above copyright
9184251Smarcel *    notice, this list of conditions and the following disclaimer.
10184251Smarcel * 2. Redistributions in binary form must reproduce the above copyright
11184251Smarcel *    notice, this list of conditions and the following disclaimer in the
12184251Smarcel *    documentation and/or other materials provided with the distribution.
13184251Smarcel * 3. Neither the name of the author nor the names of any co-contributors
14184251Smarcel *    may be used to endorse or promote products derived from this software
15184251Smarcel *    without specific prior written permission.
16184251Smarcel *
17184251Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18184251Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19184251Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20184251Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21184251Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22184251Smarcel * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23184251Smarcel * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24184251Smarcel * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25184251Smarcel * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26184251Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27184251Smarcel * SUCH DAMAGE.
28184251Smarcel */
29184251Smarcel
30184251Smarcel#include <sys/cdefs.h>
31184251Smarcel__FBSDID("$FreeBSD: head/sys/dev/cfi/cfi_core.c 251118 2013-05-30 01:22:50Z brooks $");
32184251Smarcel
33188156Ssam#include "opt_cfi.h"
34188156Ssam
35184251Smarcel#include <sys/param.h>
36184251Smarcel#include <sys/systm.h>
37184251Smarcel#include <sys/bus.h>
38184251Smarcel#include <sys/conf.h>
39233553Sjchandra#include <sys/endian.h>
40250115Sbrooks#include <sys/kenv.h>
41184251Smarcel#include <sys/kernel.h>
42184251Smarcel#include <sys/malloc.h>
43184251Smarcel#include <sys/module.h>
44184251Smarcel#include <sys/rman.h>
45184251Smarcel#include <sys/sysctl.h>
46184251Smarcel
47184251Smarcel#include <machine/bus.h>
48184251Smarcel
49184251Smarcel#include <dev/cfi/cfi_reg.h>
50184251Smarcel#include <dev/cfi/cfi_var.h>
51184251Smarcel
52184251Smarcelextern struct cdevsw cfi_cdevsw;
53184251Smarcel
54184251Smarcelchar cfi_driver_name[] = "cfi";
55184251Smarceldevclass_t cfi_devclass;
56189606Ssamdevclass_t cfi_diskclass;
57184251Smarcel
58184251Smarceluint32_t
59233553Sjchandracfi_read_raw(struct cfi_softc *sc, u_int ofs)
60184251Smarcel{
61184251Smarcel	uint32_t val;
62184251Smarcel
63184251Smarcel	ofs &= ~(sc->sc_width - 1);
64184251Smarcel	switch (sc->sc_width) {
65184251Smarcel	case 1:
66184251Smarcel		val = bus_space_read_1(sc->sc_tag, sc->sc_handle, ofs);
67184251Smarcel		break;
68184251Smarcel	case 2:
69184251Smarcel		val = bus_space_read_2(sc->sc_tag, sc->sc_handle, ofs);
70184251Smarcel		break;
71184251Smarcel	case 4:
72184251Smarcel		val = bus_space_read_4(sc->sc_tag, sc->sc_handle, ofs);
73184251Smarcel		break;
74184251Smarcel	default:
75184251Smarcel		val = ~0;
76184251Smarcel		break;
77184251Smarcel	}
78184251Smarcel	return (val);
79184251Smarcel}
80184251Smarcel
81233553Sjchandrauint32_t
82233553Sjchandracfi_read(struct cfi_softc *sc, u_int ofs)
83233553Sjchandra{
84233553Sjchandra	uint32_t val;
85233553Sjchandra	uint16_t sval;
86233553Sjchandra
87233553Sjchandra	ofs &= ~(sc->sc_width - 1);
88233553Sjchandra	switch (sc->sc_width) {
89233553Sjchandra	case 1:
90233553Sjchandra		val = bus_space_read_1(sc->sc_tag, sc->sc_handle, ofs);
91233553Sjchandra		break;
92233553Sjchandra	case 2:
93233553Sjchandra		sval = bus_space_read_2(sc->sc_tag, sc->sc_handle, ofs);
94233553Sjchandra		val = le16toh(sval);
95233553Sjchandra		break;
96233553Sjchandra	case 4:
97233553Sjchandra		val = bus_space_read_4(sc->sc_tag, sc->sc_handle, ofs);
98233553Sjchandra		val = le32toh(val);
99233553Sjchandra		break;
100233553Sjchandra	default:
101233553Sjchandra		val = ~0;
102233553Sjchandra		break;
103233553Sjchandra	}
104233553Sjchandra	return (val);
105233553Sjchandra}
106233553Sjchandra
107184251Smarcelstatic void
108184251Smarcelcfi_write(struct cfi_softc *sc, u_int ofs, u_int val)
109184251Smarcel{
110184251Smarcel
111184251Smarcel	ofs &= ~(sc->sc_width - 1);
112184251Smarcel	switch (sc->sc_width) {
113184251Smarcel	case 1:
114184251Smarcel		bus_space_write_1(sc->sc_tag, sc->sc_handle, ofs, val);
115184251Smarcel		break;
116184251Smarcel	case 2:
117233553Sjchandra		bus_space_write_2(sc->sc_tag, sc->sc_handle, ofs, htole16(val));
118184251Smarcel		break;
119184251Smarcel	case 4:
120233553Sjchandra		bus_space_write_4(sc->sc_tag, sc->sc_handle, ofs, htole32(val));
121184251Smarcel		break;
122184251Smarcel	}
123184251Smarcel}
124184251Smarcel
125184251Smarceluint8_t
126184251Smarcelcfi_read_qry(struct cfi_softc *sc, u_int ofs)
127184251Smarcel{
128184251Smarcel	uint8_t val;
129184251Smarcel
130184251Smarcel	cfi_write(sc, CFI_QRY_CMD_ADDR * sc->sc_width, CFI_QRY_CMD_DATA);
131184251Smarcel	val = cfi_read(sc, ofs * sc->sc_width);
132184251Smarcel	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
133184251Smarcel	return (val);
134184251Smarcel}
135184251Smarcel
136184251Smarcelstatic void
137184251Smarcelcfi_amd_write(struct cfi_softc *sc, u_int ofs, u_int addr, u_int data)
138184251Smarcel{
139184251Smarcel
140184251Smarcel	cfi_write(sc, ofs + AMD_ADDR_START, CFI_AMD_UNLOCK);
141184251Smarcel	cfi_write(sc, ofs + AMD_ADDR_ACK, CFI_AMD_UNLOCK_ACK);
142184251Smarcel	cfi_write(sc, ofs + addr, data);
143184251Smarcel}
144184251Smarcel
145184251Smarcelstatic char *
146184251Smarcelcfi_fmtsize(uint32_t sz)
147184251Smarcel{
148184251Smarcel	static char buf[8];
149184251Smarcel	static const char *sfx[] = { "", "K", "M", "G" };
150184251Smarcel	int sfxidx;
151184251Smarcel
152184251Smarcel	sfxidx = 0;
153184251Smarcel	while (sfxidx < 3 && sz > 1023) {
154184251Smarcel		sz /= 1024;
155184251Smarcel		sfxidx++;
156184251Smarcel	}
157184251Smarcel
158184251Smarcel	sprintf(buf, "%u%sB", sz, sfx[sfxidx]);
159184251Smarcel	return (buf);
160184251Smarcel}
161184251Smarcel
162184251Smarcelint
163184251Smarcelcfi_probe(device_t dev)
164184251Smarcel{
165184251Smarcel	char desc[80];
166184251Smarcel	struct cfi_softc *sc;
167184251Smarcel	char *vend_str;
168184251Smarcel	int error;
169184251Smarcel	uint16_t iface, vend;
170184251Smarcel
171184251Smarcel	sc = device_get_softc(dev);
172184251Smarcel	sc->sc_dev = dev;
173184251Smarcel
174184251Smarcel	sc->sc_rid = 0;
175184251Smarcel	sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid,
176184251Smarcel	    RF_ACTIVE);
177184251Smarcel	if (sc->sc_res == NULL)
178184251Smarcel		return (ENXIO);
179184251Smarcel
180184251Smarcel	sc->sc_tag = rman_get_bustag(sc->sc_res);
181184251Smarcel	sc->sc_handle = rman_get_bushandle(sc->sc_res);
182184251Smarcel
183188087Ssam	if (sc->sc_width == 0) {
184188087Ssam		sc->sc_width = 1;
185188087Ssam		while (sc->sc_width <= 4) {
186188087Ssam			if (cfi_read_qry(sc, CFI_QRY_IDENT) == 'Q')
187188087Ssam				break;
188188087Ssam			sc->sc_width <<= 1;
189188087Ssam		}
190188087Ssam	} else if (cfi_read_qry(sc, CFI_QRY_IDENT) != 'Q') {
191188087Ssam		error = ENXIO;
192188087Ssam		goto out;
193184251Smarcel	}
194184251Smarcel	if (sc->sc_width > 4) {
195184251Smarcel		error = ENXIO;
196184251Smarcel		goto out;
197184251Smarcel	}
198184251Smarcel
199184251Smarcel	/* We got a Q. Check if we also have the R and the Y. */
200184251Smarcel	if (cfi_read_qry(sc, CFI_QRY_IDENT + 1) != 'R' ||
201184251Smarcel	    cfi_read_qry(sc, CFI_QRY_IDENT + 2) != 'Y') {
202184251Smarcel		error = ENXIO;
203184251Smarcel		goto out;
204184251Smarcel	}
205184251Smarcel
206184251Smarcel	/* Get the vendor and command set. */
207184251Smarcel	vend = cfi_read_qry(sc, CFI_QRY_VEND) |
208184251Smarcel	    (cfi_read_qry(sc, CFI_QRY_VEND + 1) << 8);
209184251Smarcel
210184251Smarcel	sc->sc_cmdset = vend;
211184251Smarcel
212184251Smarcel	switch (vend) {
213184251Smarcel	case CFI_VEND_AMD_ECS:
214184251Smarcel	case CFI_VEND_AMD_SCS:
215184251Smarcel		vend_str = "AMD/Fujitsu";
216184251Smarcel		break;
217184251Smarcel	case CFI_VEND_INTEL_ECS:
218184251Smarcel		vend_str = "Intel/Sharp";
219184251Smarcel		break;
220184251Smarcel	case CFI_VEND_INTEL_SCS:
221184251Smarcel		vend_str = "Intel";
222184251Smarcel		break;
223184251Smarcel	case CFI_VEND_MITSUBISHI_ECS:
224184251Smarcel	case CFI_VEND_MITSUBISHI_SCS:
225184251Smarcel		vend_str = "Mitsubishi";
226184251Smarcel		break;
227184251Smarcel	default:
228184251Smarcel		vend_str = "Unknown vendor";
229184251Smarcel		break;
230184251Smarcel	}
231184251Smarcel
232184251Smarcel	/* Get the device size. */
233184251Smarcel	sc->sc_size = 1U << cfi_read_qry(sc, CFI_QRY_SIZE);
234184251Smarcel
235184251Smarcel	/* Sanity-check the I/F */
236184251Smarcel	iface = cfi_read_qry(sc, CFI_QRY_IFACE) |
237184251Smarcel	    (cfi_read_qry(sc, CFI_QRY_IFACE + 1) << 8);
238184251Smarcel
239184251Smarcel	/*
240184251Smarcel	 * Adding 1 to iface will give us a bit-wise "switch"
241184251Smarcel	 * that allows us to test for the interface width by
242184251Smarcel	 * testing a single bit.
243184251Smarcel	 */
244184251Smarcel	iface++;
245184251Smarcel
246184251Smarcel	error = (iface & sc->sc_width) ? 0 : EINVAL;
247184251Smarcel	if (error)
248184251Smarcel		goto out;
249184251Smarcel
250184251Smarcel	snprintf(desc, sizeof(desc), "%s - %s", vend_str,
251184251Smarcel	    cfi_fmtsize(sc->sc_size));
252184251Smarcel	device_set_desc_copy(dev, desc);
253184251Smarcel
254184251Smarcel out:
255184251Smarcel	bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res);
256184251Smarcel	return (error);
257184251Smarcel}
258184251Smarcel
259184251Smarcelint
260184251Smarcelcfi_attach(device_t dev)
261184251Smarcel{
262184251Smarcel	struct cfi_softc *sc;
263184251Smarcel	u_int blksz, blocks;
264184251Smarcel	u_int r, u;
265250115Sbrooks#ifdef CFI_SUPPORT_STRATAFLASH
266250115Sbrooks	uint64_t ppr;
267250115Sbrooks	char name[KENV_MNAMELEN], value[32];
268250115Sbrooks#endif
269184251Smarcel
270184251Smarcel	sc = device_get_softc(dev);
271184251Smarcel	sc->sc_dev = dev;
272184251Smarcel
273184251Smarcel	sc->sc_rid = 0;
274184251Smarcel	sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid,
275184251Smarcel	    RF_ACTIVE);
276184251Smarcel	if (sc->sc_res == NULL)
277184251Smarcel		return (ENXIO);
278184251Smarcel
279184251Smarcel	sc->sc_tag = rman_get_bustag(sc->sc_res);
280184251Smarcel	sc->sc_handle = rman_get_bushandle(sc->sc_res);
281184251Smarcel
282184251Smarcel	/* Get time-out values for erase and write. */
283184251Smarcel	sc->sc_write_timeout = 1 << cfi_read_qry(sc, CFI_QRY_TTO_WRITE);
284184251Smarcel	sc->sc_erase_timeout = 1 << cfi_read_qry(sc, CFI_QRY_TTO_ERASE);
285184251Smarcel	sc->sc_write_timeout *= 1 << cfi_read_qry(sc, CFI_QRY_MTO_WRITE);
286184251Smarcel	sc->sc_erase_timeout *= 1 << cfi_read_qry(sc, CFI_QRY_MTO_ERASE);
287184251Smarcel
288184251Smarcel	/* Get erase regions. */
289184251Smarcel	sc->sc_regions = cfi_read_qry(sc, CFI_QRY_NREGIONS);
290184251Smarcel	sc->sc_region = malloc(sc->sc_regions * sizeof(struct cfi_region),
291184251Smarcel	    M_TEMP, M_WAITOK | M_ZERO);
292184251Smarcel	for (r = 0; r < sc->sc_regions; r++) {
293184251Smarcel		blocks = cfi_read_qry(sc, CFI_QRY_REGION(r)) |
294184251Smarcel		    (cfi_read_qry(sc, CFI_QRY_REGION(r) + 1) << 8);
295184251Smarcel		sc->sc_region[r].r_blocks = blocks + 1;
296184251Smarcel
297184251Smarcel		blksz = cfi_read_qry(sc, CFI_QRY_REGION(r) + 2) |
298184251Smarcel		    (cfi_read_qry(sc, CFI_QRY_REGION(r) + 3) << 8);
299184251Smarcel		sc->sc_region[r].r_blksz = (blksz == 0) ? 128 :
300184251Smarcel		    blksz * 256;
301184251Smarcel	}
302184251Smarcel
303184251Smarcel	/* Reset the device to a default state. */
304184251Smarcel	cfi_write(sc, 0, CFI_BCS_CLEAR_STATUS);
305184251Smarcel
306184251Smarcel	if (bootverbose) {
307184251Smarcel		device_printf(dev, "[");
308184251Smarcel		for (r = 0; r < sc->sc_regions; r++) {
309184251Smarcel			printf("%ux%s%s", sc->sc_region[r].r_blocks,
310184251Smarcel			    cfi_fmtsize(sc->sc_region[r].r_blksz),
311184251Smarcel			    (r == sc->sc_regions - 1) ? "]\n" : ",");
312184251Smarcel		}
313184251Smarcel	}
314184251Smarcel
315184251Smarcel	u = device_get_unit(dev);
316184251Smarcel	sc->sc_nod = make_dev(&cfi_cdevsw, u, UID_ROOT, GID_WHEEL, 0600,
317184251Smarcel	    "%s%u", cfi_driver_name, u);
318184251Smarcel	sc->sc_nod->si_drv1 = sc;
319184251Smarcel
320250115Sbrooks#ifdef CFI_SUPPORT_STRATAFLASH
321250115Sbrooks	/*
322250115Sbrooks	 * Store the Intel factory PPR in the environment.  In some
323250115Sbrooks	 * cases it is the most unique ID on a board.
324250115Sbrooks	 */
325250115Sbrooks	if (cfi_intel_get_factory_pr(sc, &ppr) == 0) {
326250115Sbrooks		if (snprintf(name, sizeof(name), "%s.factory_ppr",
327250115Sbrooks		    device_get_nameunit(dev)) < (sizeof(name) - 1) &&
328250115Sbrooks		    snprintf(value, sizeof(value), "0x%016jx", ppr) <
329250115Sbrooks		    (sizeof(value) - 1))
330250115Sbrooks			(void) setenv(name, value);
331250115Sbrooks	}
332250115Sbrooks#endif
333250115Sbrooks
334193936Simp	device_add_child(dev, "cfid", -1);
335189606Ssam	bus_generic_attach(dev);
336189606Ssam
337184251Smarcel	return (0);
338184251Smarcel}
339184251Smarcel
340184251Smarcelint
341184251Smarcelcfi_detach(device_t dev)
342184251Smarcel{
343184251Smarcel	struct cfi_softc *sc;
344184251Smarcel
345184251Smarcel	sc = device_get_softc(dev);
346184251Smarcel
347184251Smarcel	destroy_dev(sc->sc_nod);
348184251Smarcel	free(sc->sc_region, M_TEMP);
349184251Smarcel	bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res);
350184251Smarcel	return (0);
351184251Smarcel}
352184251Smarcel
353184251Smarcelstatic int
354188156Ssamcfi_wait_ready(struct cfi_softc *sc, u_int ofs, u_int timeout)
355184251Smarcel{
356184251Smarcel	int done, error;
357188156Ssam	uint32_t st0 = 0, st = 0;
358184251Smarcel
359184251Smarcel	done = 0;
360184251Smarcel	error = 0;
361184251Smarcel	timeout *= 10;
362184251Smarcel	while (!done && !error && timeout) {
363184251Smarcel		DELAY(100);
364184251Smarcel		timeout--;
365184251Smarcel
366184251Smarcel		switch (sc->sc_cmdset) {
367184251Smarcel		case CFI_VEND_INTEL_ECS:
368184251Smarcel		case CFI_VEND_INTEL_SCS:
369188156Ssam			st = cfi_read(sc, ofs);
370188156Ssam			done = (st & CFI_INTEL_STATUS_WSMS);
371184251Smarcel			if (done) {
372188156Ssam				/* NB: bit 0 is reserved */
373188156Ssam				st &= ~(CFI_INTEL_XSTATUS_RSVD |
374188156Ssam					CFI_INTEL_STATUS_WSMS |
375188156Ssam					CFI_INTEL_STATUS_RSVD);
376188156Ssam				if (st & CFI_INTEL_STATUS_DPS)
377184251Smarcel					error = EPERM;
378188156Ssam				else if (st & CFI_INTEL_STATUS_PSLBS)
379184251Smarcel					error = EIO;
380188156Ssam				else if (st & CFI_INTEL_STATUS_ECLBS)
381184251Smarcel					error = ENXIO;
382188156Ssam				else if (st)
383188156Ssam					error = EACCES;
384184251Smarcel			}
385184251Smarcel			break;
386184251Smarcel		case CFI_VEND_AMD_SCS:
387184251Smarcel		case CFI_VEND_AMD_ECS:
388188156Ssam			st0 = cfi_read(sc, ofs);
389188156Ssam			st = cfi_read(sc, ofs);
390184251Smarcel			done = ((st & 0x40) == (st0 & 0x40)) ? 1 : 0;
391184251Smarcel			break;
392184251Smarcel		}
393184251Smarcel	}
394184251Smarcel	if (!done && !error)
395184251Smarcel		error = ETIMEDOUT;
396184251Smarcel	if (error)
397188156Ssam		printf("\nerror=%d (st 0x%x st0 0x%x)\n", error, st, st0);
398184251Smarcel	return (error);
399184251Smarcel}
400184251Smarcel
401184251Smarcelint
402184251Smarcelcfi_write_block(struct cfi_softc *sc)
403184251Smarcel{
404184251Smarcel	union {
405184251Smarcel		uint8_t		*x8;
406184251Smarcel		uint16_t	*x16;
407184251Smarcel		uint32_t	*x32;
408184251Smarcel	} ptr;
409184251Smarcel	register_t intr;
410184251Smarcel	int error, i;
411184251Smarcel
412251118Sbrooks	/* Intel flash must be unlocked before modification */
413251118Sbrooks	switch (sc->sc_cmdset) {
414251118Sbrooks	case CFI_VEND_INTEL_ECS:
415251118Sbrooks	case CFI_VEND_INTEL_SCS:
416251118Sbrooks		cfi_write(sc, sc->sc_wrofs, CFI_INTEL_LBS);
417251118Sbrooks		cfi_write(sc, sc->sc_wrofs, CFI_INTEL_UB);
418251118Sbrooks		cfi_write(sc, sc->sc_wrofs, CFI_BCS_READ_ARRAY);
419251118Sbrooks		break;
420251118Sbrooks	}
421251118Sbrooks
422184251Smarcel	/* Erase the block. */
423184251Smarcel	switch (sc->sc_cmdset) {
424184251Smarcel	case CFI_VEND_INTEL_ECS:
425184251Smarcel	case CFI_VEND_INTEL_SCS:
426184251Smarcel		cfi_write(sc, sc->sc_wrofs, CFI_BCS_BLOCK_ERASE);
427184251Smarcel		cfi_write(sc, sc->sc_wrofs, CFI_BCS_CONFIRM);
428184251Smarcel		break;
429184251Smarcel	case CFI_VEND_AMD_SCS:
430184251Smarcel	case CFI_VEND_AMD_ECS:
431184251Smarcel		cfi_amd_write(sc, sc->sc_wrofs, AMD_ADDR_START,
432184251Smarcel		    CFI_AMD_ERASE_SECTOR);
433184251Smarcel		cfi_amd_write(sc, sc->sc_wrofs, 0, CFI_AMD_BLOCK_ERASE);
434184251Smarcel		break;
435184251Smarcel	default:
436184251Smarcel		/* Better safe than sorry... */
437184251Smarcel		return (ENODEV);
438184251Smarcel	}
439188156Ssam	error = cfi_wait_ready(sc, sc->sc_wrofs, sc->sc_erase_timeout);
440184251Smarcel	if (error)
441184251Smarcel		goto out;
442184251Smarcel
443184251Smarcel	/* Write the block. */
444184251Smarcel	ptr.x8 = sc->sc_wrbuf;
445184251Smarcel	for (i = 0; i < sc->sc_wrbufsz; i += sc->sc_width) {
446184251Smarcel
447184251Smarcel		/*
448184251Smarcel		 * Make sure the command to start a write and the
449184251Smarcel		 * actual write happens back-to-back without any
450184251Smarcel		 * excessive delays.
451184251Smarcel		 */
452184251Smarcel		intr = intr_disable();
453184251Smarcel
454184251Smarcel		switch (sc->sc_cmdset) {
455184251Smarcel		case CFI_VEND_INTEL_ECS:
456184251Smarcel		case CFI_VEND_INTEL_SCS:
457184251Smarcel			cfi_write(sc, sc->sc_wrofs + i, CFI_BCS_PROGRAM);
458184251Smarcel			break;
459184251Smarcel		case CFI_VEND_AMD_SCS:
460184251Smarcel		case CFI_VEND_AMD_ECS:
461184251Smarcel			cfi_amd_write(sc, 0, AMD_ADDR_START, CFI_AMD_PROGRAM);
462184251Smarcel			break;
463184251Smarcel		}
464184251Smarcel		switch (sc->sc_width) {
465184251Smarcel		case 1:
466184251Smarcel			bus_space_write_1(sc->sc_tag, sc->sc_handle,
467184251Smarcel			    sc->sc_wrofs + i, *(ptr.x8)++);
468184251Smarcel			break;
469184251Smarcel		case 2:
470184251Smarcel			bus_space_write_2(sc->sc_tag, sc->sc_handle,
471184251Smarcel			    sc->sc_wrofs + i, *(ptr.x16)++);
472184251Smarcel			break;
473184251Smarcel		case 4:
474184251Smarcel			bus_space_write_4(sc->sc_tag, sc->sc_handle,
475184251Smarcel			    sc->sc_wrofs + i, *(ptr.x32)++);
476184251Smarcel			break;
477184251Smarcel		}
478184251Smarcel
479184251Smarcel		intr_restore(intr);
480184251Smarcel
481188156Ssam		error = cfi_wait_ready(sc, sc->sc_wrofs, sc->sc_write_timeout);
482184251Smarcel		if (error)
483184251Smarcel			goto out;
484184251Smarcel	}
485184251Smarcel
486184251Smarcel	/* error is 0. */
487184251Smarcel
488184251Smarcel out:
489184251Smarcel	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
490251118Sbrooks
491251118Sbrooks	/* Relock Intel flash */
492251118Sbrooks	switch (sc->sc_cmdset) {
493251118Sbrooks	case CFI_VEND_INTEL_ECS:
494251118Sbrooks	case CFI_VEND_INTEL_SCS:
495251118Sbrooks		cfi_write(sc, sc->sc_wrofs, CFI_INTEL_LBS);
496251118Sbrooks		cfi_write(sc, sc->sc_wrofs, CFI_INTEL_LB);
497251118Sbrooks		cfi_write(sc, sc->sc_wrofs, CFI_BCS_READ_ARRAY);
498251118Sbrooks		break;
499251118Sbrooks	}
500184251Smarcel	return (error);
501184251Smarcel}
502188156Ssam
503188156Ssam#ifdef CFI_SUPPORT_STRATAFLASH
504188156Ssam/*
505188156Ssam * Intel StrataFlash Protection Register Support.
506188156Ssam *
507188156Ssam * The memory includes a 128-bit Protection Register that can be
508188156Ssam * used for security.  There are two 64-bit segments; one is programmed
509188156Ssam * at the factory with a unique 64-bit number which is immutable.
510188156Ssam * The other segment is left blank for User (OEM) programming.
511188267Ssam * The User/OEM segment is One Time Programmable (OTP).  It can also
512188332Ssam * be locked to prevent any further writes by setting bit 0 of the
513188267Ssam * Protection Lock Register (PLR).  The PLR can written only once.
514188156Ssam */
515188156Ssam
516188156Ssamstatic uint16_t
517188156Ssamcfi_get16(struct cfi_softc *sc, int off)
518188156Ssam{
519188156Ssam	uint16_t v = bus_space_read_2(sc->sc_tag, sc->sc_handle, off<<1);
520188156Ssam	return v;
521188156Ssam}
522188156Ssam
523188268Ssam#ifdef CFI_ARMEDANDDANGEROUS
524188156Ssamstatic void
525188156Ssamcfi_put16(struct cfi_softc *sc, int off, uint16_t v)
526188156Ssam{
527188156Ssam	bus_space_write_2(sc->sc_tag, sc->sc_handle, off<<1, v);
528188156Ssam}
529188268Ssam#endif
530188156Ssam
531188156Ssam/*
532188156Ssam * Read the factory-defined 64-bit segment of the PR.
533188156Ssam */
534188156Ssamint
535188156Ssamcfi_intel_get_factory_pr(struct cfi_softc *sc, uint64_t *id)
536188156Ssam{
537188156Ssam	if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
538188156Ssam		return EOPNOTSUPP;
539188156Ssam	KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
540188156Ssam
541188156Ssam	cfi_write(sc, 0, CFI_INTEL_READ_ID);
542188156Ssam	*id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(0)))<<48 |
543188156Ssam	      ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(1)))<<32 |
544188156Ssam	      ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(2)))<<16 |
545188156Ssam	      ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(3)));
546188156Ssam	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
547188156Ssam	return 0;
548188156Ssam}
549188156Ssam
550188156Ssam/*
551188156Ssam * Read the User/OEM 64-bit segment of the PR.
552188156Ssam */
553188156Ssamint
554188156Ssamcfi_intel_get_oem_pr(struct cfi_softc *sc, uint64_t *id)
555188156Ssam{
556188156Ssam	if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
557188156Ssam		return EOPNOTSUPP;
558188156Ssam	KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
559188156Ssam
560188156Ssam	cfi_write(sc, 0, CFI_INTEL_READ_ID);
561188156Ssam	*id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(4)))<<48 |
562188156Ssam	      ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(5)))<<32 |
563188156Ssam	      ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(6)))<<16 |
564188156Ssam	      ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(7)));
565188156Ssam	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
566188156Ssam	return 0;
567188156Ssam}
568188156Ssam
569188156Ssam/*
570188156Ssam * Write the User/OEM 64-bit segment of the PR.
571188267Ssam * XXX should allow writing individual words/bytes
572188156Ssam */
573188156Ssamint
574188156Ssamcfi_intel_set_oem_pr(struct cfi_softc *sc, uint64_t id)
575188156Ssam{
576188267Ssam#ifdef CFI_ARMEDANDDANGEROUS
577188156Ssam	register_t intr;
578188156Ssam	int i, error;
579188267Ssam#endif
580188156Ssam
581188156Ssam	if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
582188156Ssam		return EOPNOTSUPP;
583188156Ssam	KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
584188156Ssam
585188267Ssam#ifdef CFI_ARMEDANDDANGEROUS
586188156Ssam	for (i = 7; i >= 4; i--, id >>= 16) {
587188156Ssam		intr = intr_disable();
588188156Ssam		cfi_write(sc, 0, CFI_INTEL_PP_SETUP);
589188156Ssam		cfi_put16(sc, CFI_INTEL_PR(i), id&0xffff);
590188156Ssam		intr_restore(intr);
591188156Ssam		error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS,
592188156Ssam		    sc->sc_write_timeout);
593188156Ssam		if (error)
594188156Ssam			break;
595188156Ssam	}
596188156Ssam	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
597188156Ssam	return error;
598188267Ssam#else
599188267Ssam	device_printf(sc->sc_dev, "%s: OEM PR not set, "
600188267Ssam	    "CFI_ARMEDANDDANGEROUS not configured\n", __func__);
601188267Ssam	return ENXIO;
602188267Ssam#endif
603188156Ssam}
604188156Ssam
605188156Ssam/*
606188156Ssam * Read the contents of the Protection Lock Register.
607188156Ssam */
608188156Ssamint
609188156Ssamcfi_intel_get_plr(struct cfi_softc *sc, uint32_t *plr)
610188156Ssam{
611188156Ssam	if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
612188156Ssam		return EOPNOTSUPP;
613188156Ssam	KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
614188156Ssam
615188156Ssam	cfi_write(sc, 0, CFI_INTEL_READ_ID);
616188156Ssam	*plr = cfi_get16(sc, CFI_INTEL_PLR);
617188156Ssam	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
618188156Ssam	return 0;
619188156Ssam}
620188156Ssam
621188156Ssam/*
622188156Ssam * Write the Protection Lock Register to lock down the
623188156Ssam * user-settable segment of the Protection Register.
624188156Ssam * NOTE: this operation is not reversible.
625188156Ssam */
626188156Ssamint
627188156Ssamcfi_intel_set_plr(struct cfi_softc *sc)
628188156Ssam{
629188156Ssam#ifdef CFI_ARMEDANDDANGEROUS
630188156Ssam	register_t intr;
631188268Ssam	int error;
632188156Ssam#endif
633188156Ssam	if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
634188156Ssam		return EOPNOTSUPP;
635188156Ssam	KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
636188156Ssam
637188156Ssam#ifdef CFI_ARMEDANDDANGEROUS
638188156Ssam	/* worthy of console msg */
639188156Ssam	device_printf(sc->sc_dev, "set PLR\n");
640188156Ssam	intr = intr_disable();
641188156Ssam	cfi_write(sc, 0, CFI_INTEL_PP_SETUP);
642188156Ssam	cfi_put16(sc, CFI_INTEL_PLR, 0xFFFD);
643188156Ssam	intr_restore(intr);
644188156Ssam	error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, sc->sc_write_timeout);
645188156Ssam	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
646188268Ssam	return error;
647188156Ssam#else
648188156Ssam	device_printf(sc->sc_dev, "%s: PLR not set, "
649188156Ssam	    "CFI_ARMEDANDDANGEROUS not configured\n", __func__);
650188268Ssam	return ENXIO;
651188156Ssam#endif
652188156Ssam}
653188156Ssam#endif /* CFI_SUPPORT_STRATAFLASH */
654