cfi_core.c revision 233553
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 233553 2012-03-27 15:13:12Z jchandra $");
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>
40184251Smarcel#include <sys/kernel.h>
41184251Smarcel#include <sys/malloc.h>
42184251Smarcel#include <sys/module.h>
43184251Smarcel#include <sys/rman.h>
44184251Smarcel#include <sys/sysctl.h>
45184251Smarcel
46184251Smarcel#include <machine/bus.h>
47184251Smarcel
48184251Smarcel#include <dev/cfi/cfi_reg.h>
49184251Smarcel#include <dev/cfi/cfi_var.h>
50184251Smarcel
51184251Smarcelextern struct cdevsw cfi_cdevsw;
52184251Smarcel
53184251Smarcelchar cfi_driver_name[] = "cfi";
54184251Smarceldevclass_t cfi_devclass;
55189606Ssamdevclass_t cfi_diskclass;
56184251Smarcel
57184251Smarceluint32_t
58233553Sjchandracfi_read_raw(struct cfi_softc *sc, u_int ofs)
59184251Smarcel{
60184251Smarcel	uint32_t val;
61184251Smarcel
62184251Smarcel	ofs &= ~(sc->sc_width - 1);
63184251Smarcel	switch (sc->sc_width) {
64184251Smarcel	case 1:
65184251Smarcel		val = bus_space_read_1(sc->sc_tag, sc->sc_handle, ofs);
66184251Smarcel		break;
67184251Smarcel	case 2:
68184251Smarcel		val = bus_space_read_2(sc->sc_tag, sc->sc_handle, ofs);
69184251Smarcel		break;
70184251Smarcel	case 4:
71184251Smarcel		val = bus_space_read_4(sc->sc_tag, sc->sc_handle, ofs);
72184251Smarcel		break;
73184251Smarcel	default:
74184251Smarcel		val = ~0;
75184251Smarcel		break;
76184251Smarcel	}
77184251Smarcel	return (val);
78184251Smarcel}
79184251Smarcel
80233553Sjchandrauint32_t
81233553Sjchandracfi_read(struct cfi_softc *sc, u_int ofs)
82233553Sjchandra{
83233553Sjchandra	uint32_t val;
84233553Sjchandra	uint16_t sval;
85233553Sjchandra
86233553Sjchandra	ofs &= ~(sc->sc_width - 1);
87233553Sjchandra	switch (sc->sc_width) {
88233553Sjchandra	case 1:
89233553Sjchandra		val = bus_space_read_1(sc->sc_tag, sc->sc_handle, ofs);
90233553Sjchandra		break;
91233553Sjchandra	case 2:
92233553Sjchandra		sval = bus_space_read_2(sc->sc_tag, sc->sc_handle, ofs);
93233553Sjchandra		val = le16toh(sval);
94233553Sjchandra		break;
95233553Sjchandra	case 4:
96233553Sjchandra		val = bus_space_read_4(sc->sc_tag, sc->sc_handle, ofs);
97233553Sjchandra		val = le32toh(val);
98233553Sjchandra		break;
99233553Sjchandra	default:
100233553Sjchandra		val = ~0;
101233553Sjchandra		break;
102233553Sjchandra	}
103233553Sjchandra	return (val);
104233553Sjchandra}
105233553Sjchandra
106184251Smarcelstatic void
107184251Smarcelcfi_write(struct cfi_softc *sc, u_int ofs, u_int val)
108184251Smarcel{
109184251Smarcel
110184251Smarcel	ofs &= ~(sc->sc_width - 1);
111184251Smarcel	switch (sc->sc_width) {
112184251Smarcel	case 1:
113184251Smarcel		bus_space_write_1(sc->sc_tag, sc->sc_handle, ofs, val);
114184251Smarcel		break;
115184251Smarcel	case 2:
116233553Sjchandra		bus_space_write_2(sc->sc_tag, sc->sc_handle, ofs, htole16(val));
117184251Smarcel		break;
118184251Smarcel	case 4:
119233553Sjchandra		bus_space_write_4(sc->sc_tag, sc->sc_handle, ofs, htole32(val));
120184251Smarcel		break;
121184251Smarcel	}
122184251Smarcel}
123184251Smarcel
124184251Smarceluint8_t
125184251Smarcelcfi_read_qry(struct cfi_softc *sc, u_int ofs)
126184251Smarcel{
127184251Smarcel	uint8_t val;
128184251Smarcel
129184251Smarcel	cfi_write(sc, CFI_QRY_CMD_ADDR * sc->sc_width, CFI_QRY_CMD_DATA);
130184251Smarcel	val = cfi_read(sc, ofs * sc->sc_width);
131184251Smarcel	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
132184251Smarcel	return (val);
133184251Smarcel}
134184251Smarcel
135184251Smarcelstatic void
136184251Smarcelcfi_amd_write(struct cfi_softc *sc, u_int ofs, u_int addr, u_int data)
137184251Smarcel{
138184251Smarcel
139184251Smarcel	cfi_write(sc, ofs + AMD_ADDR_START, CFI_AMD_UNLOCK);
140184251Smarcel	cfi_write(sc, ofs + AMD_ADDR_ACK, CFI_AMD_UNLOCK_ACK);
141184251Smarcel	cfi_write(sc, ofs + addr, data);
142184251Smarcel}
143184251Smarcel
144184251Smarcelstatic char *
145184251Smarcelcfi_fmtsize(uint32_t sz)
146184251Smarcel{
147184251Smarcel	static char buf[8];
148184251Smarcel	static const char *sfx[] = { "", "K", "M", "G" };
149184251Smarcel	int sfxidx;
150184251Smarcel
151184251Smarcel	sfxidx = 0;
152184251Smarcel	while (sfxidx < 3 && sz > 1023) {
153184251Smarcel		sz /= 1024;
154184251Smarcel		sfxidx++;
155184251Smarcel	}
156184251Smarcel
157184251Smarcel	sprintf(buf, "%u%sB", sz, sfx[sfxidx]);
158184251Smarcel	return (buf);
159184251Smarcel}
160184251Smarcel
161184251Smarcelint
162184251Smarcelcfi_probe(device_t dev)
163184251Smarcel{
164184251Smarcel	char desc[80];
165184251Smarcel	struct cfi_softc *sc;
166184251Smarcel	char *vend_str;
167184251Smarcel	int error;
168184251Smarcel	uint16_t iface, vend;
169184251Smarcel
170184251Smarcel	sc = device_get_softc(dev);
171184251Smarcel	sc->sc_dev = dev;
172184251Smarcel
173184251Smarcel	sc->sc_rid = 0;
174184251Smarcel	sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid,
175184251Smarcel	    RF_ACTIVE);
176184251Smarcel	if (sc->sc_res == NULL)
177184251Smarcel		return (ENXIO);
178184251Smarcel
179184251Smarcel	sc->sc_tag = rman_get_bustag(sc->sc_res);
180184251Smarcel	sc->sc_handle = rman_get_bushandle(sc->sc_res);
181184251Smarcel
182188087Ssam	if (sc->sc_width == 0) {
183188087Ssam		sc->sc_width = 1;
184188087Ssam		while (sc->sc_width <= 4) {
185188087Ssam			if (cfi_read_qry(sc, CFI_QRY_IDENT) == 'Q')
186188087Ssam				break;
187188087Ssam			sc->sc_width <<= 1;
188188087Ssam		}
189188087Ssam	} else if (cfi_read_qry(sc, CFI_QRY_IDENT) != 'Q') {
190188087Ssam		error = ENXIO;
191188087Ssam		goto out;
192184251Smarcel	}
193184251Smarcel	if (sc->sc_width > 4) {
194184251Smarcel		error = ENXIO;
195184251Smarcel		goto out;
196184251Smarcel	}
197184251Smarcel
198184251Smarcel	/* We got a Q. Check if we also have the R and the Y. */
199184251Smarcel	if (cfi_read_qry(sc, CFI_QRY_IDENT + 1) != 'R' ||
200184251Smarcel	    cfi_read_qry(sc, CFI_QRY_IDENT + 2) != 'Y') {
201184251Smarcel		error = ENXIO;
202184251Smarcel		goto out;
203184251Smarcel	}
204184251Smarcel
205184251Smarcel	/* Get the vendor and command set. */
206184251Smarcel	vend = cfi_read_qry(sc, CFI_QRY_VEND) |
207184251Smarcel	    (cfi_read_qry(sc, CFI_QRY_VEND + 1) << 8);
208184251Smarcel
209184251Smarcel	sc->sc_cmdset = vend;
210184251Smarcel
211184251Smarcel	switch (vend) {
212184251Smarcel	case CFI_VEND_AMD_ECS:
213184251Smarcel	case CFI_VEND_AMD_SCS:
214184251Smarcel		vend_str = "AMD/Fujitsu";
215184251Smarcel		break;
216184251Smarcel	case CFI_VEND_INTEL_ECS:
217184251Smarcel		vend_str = "Intel/Sharp";
218184251Smarcel		break;
219184251Smarcel	case CFI_VEND_INTEL_SCS:
220184251Smarcel		vend_str = "Intel";
221184251Smarcel		break;
222184251Smarcel	case CFI_VEND_MITSUBISHI_ECS:
223184251Smarcel	case CFI_VEND_MITSUBISHI_SCS:
224184251Smarcel		vend_str = "Mitsubishi";
225184251Smarcel		break;
226184251Smarcel	default:
227184251Smarcel		vend_str = "Unknown vendor";
228184251Smarcel		break;
229184251Smarcel	}
230184251Smarcel
231184251Smarcel	/* Get the device size. */
232184251Smarcel	sc->sc_size = 1U << cfi_read_qry(sc, CFI_QRY_SIZE);
233184251Smarcel
234184251Smarcel	/* Sanity-check the I/F */
235184251Smarcel	iface = cfi_read_qry(sc, CFI_QRY_IFACE) |
236184251Smarcel	    (cfi_read_qry(sc, CFI_QRY_IFACE + 1) << 8);
237184251Smarcel
238184251Smarcel	/*
239184251Smarcel	 * Adding 1 to iface will give us a bit-wise "switch"
240184251Smarcel	 * that allows us to test for the interface width by
241184251Smarcel	 * testing a single bit.
242184251Smarcel	 */
243184251Smarcel	iface++;
244184251Smarcel
245184251Smarcel	error = (iface & sc->sc_width) ? 0 : EINVAL;
246184251Smarcel	if (error)
247184251Smarcel		goto out;
248184251Smarcel
249184251Smarcel	snprintf(desc, sizeof(desc), "%s - %s", vend_str,
250184251Smarcel	    cfi_fmtsize(sc->sc_size));
251184251Smarcel	device_set_desc_copy(dev, desc);
252184251Smarcel
253184251Smarcel out:
254184251Smarcel	bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res);
255184251Smarcel	return (error);
256184251Smarcel}
257184251Smarcel
258184251Smarcelint
259184251Smarcelcfi_attach(device_t dev)
260184251Smarcel{
261184251Smarcel	struct cfi_softc *sc;
262184251Smarcel	u_int blksz, blocks;
263184251Smarcel	u_int r, u;
264184251Smarcel
265184251Smarcel	sc = device_get_softc(dev);
266184251Smarcel	sc->sc_dev = dev;
267184251Smarcel
268184251Smarcel	sc->sc_rid = 0;
269184251Smarcel	sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid,
270184251Smarcel	    RF_ACTIVE);
271184251Smarcel	if (sc->sc_res == NULL)
272184251Smarcel		return (ENXIO);
273184251Smarcel
274184251Smarcel	sc->sc_tag = rman_get_bustag(sc->sc_res);
275184251Smarcel	sc->sc_handle = rman_get_bushandle(sc->sc_res);
276184251Smarcel
277184251Smarcel	/* Get time-out values for erase and write. */
278184251Smarcel	sc->sc_write_timeout = 1 << cfi_read_qry(sc, CFI_QRY_TTO_WRITE);
279184251Smarcel	sc->sc_erase_timeout = 1 << cfi_read_qry(sc, CFI_QRY_TTO_ERASE);
280184251Smarcel	sc->sc_write_timeout *= 1 << cfi_read_qry(sc, CFI_QRY_MTO_WRITE);
281184251Smarcel	sc->sc_erase_timeout *= 1 << cfi_read_qry(sc, CFI_QRY_MTO_ERASE);
282184251Smarcel
283184251Smarcel	/* Get erase regions. */
284184251Smarcel	sc->sc_regions = cfi_read_qry(sc, CFI_QRY_NREGIONS);
285184251Smarcel	sc->sc_region = malloc(sc->sc_regions * sizeof(struct cfi_region),
286184251Smarcel	    M_TEMP, M_WAITOK | M_ZERO);
287184251Smarcel	for (r = 0; r < sc->sc_regions; r++) {
288184251Smarcel		blocks = cfi_read_qry(sc, CFI_QRY_REGION(r)) |
289184251Smarcel		    (cfi_read_qry(sc, CFI_QRY_REGION(r) + 1) << 8);
290184251Smarcel		sc->sc_region[r].r_blocks = blocks + 1;
291184251Smarcel
292184251Smarcel		blksz = cfi_read_qry(sc, CFI_QRY_REGION(r) + 2) |
293184251Smarcel		    (cfi_read_qry(sc, CFI_QRY_REGION(r) + 3) << 8);
294184251Smarcel		sc->sc_region[r].r_blksz = (blksz == 0) ? 128 :
295184251Smarcel		    blksz * 256;
296184251Smarcel	}
297184251Smarcel
298184251Smarcel	/* Reset the device to a default state. */
299184251Smarcel	cfi_write(sc, 0, CFI_BCS_CLEAR_STATUS);
300184251Smarcel
301184251Smarcel	if (bootverbose) {
302184251Smarcel		device_printf(dev, "[");
303184251Smarcel		for (r = 0; r < sc->sc_regions; r++) {
304184251Smarcel			printf("%ux%s%s", sc->sc_region[r].r_blocks,
305184251Smarcel			    cfi_fmtsize(sc->sc_region[r].r_blksz),
306184251Smarcel			    (r == sc->sc_regions - 1) ? "]\n" : ",");
307184251Smarcel		}
308184251Smarcel	}
309184251Smarcel
310184251Smarcel	u = device_get_unit(dev);
311184251Smarcel	sc->sc_nod = make_dev(&cfi_cdevsw, u, UID_ROOT, GID_WHEEL, 0600,
312184251Smarcel	    "%s%u", cfi_driver_name, u);
313184251Smarcel	sc->sc_nod->si_drv1 = sc;
314184251Smarcel
315193936Simp	device_add_child(dev, "cfid", -1);
316189606Ssam	bus_generic_attach(dev);
317189606Ssam
318184251Smarcel	return (0);
319184251Smarcel}
320184251Smarcel
321184251Smarcelint
322184251Smarcelcfi_detach(device_t dev)
323184251Smarcel{
324184251Smarcel	struct cfi_softc *sc;
325184251Smarcel
326184251Smarcel	sc = device_get_softc(dev);
327184251Smarcel
328184251Smarcel	destroy_dev(sc->sc_nod);
329184251Smarcel	free(sc->sc_region, M_TEMP);
330184251Smarcel	bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res);
331184251Smarcel	return (0);
332184251Smarcel}
333184251Smarcel
334184251Smarcelstatic int
335188156Ssamcfi_wait_ready(struct cfi_softc *sc, u_int ofs, u_int timeout)
336184251Smarcel{
337184251Smarcel	int done, error;
338188156Ssam	uint32_t st0 = 0, st = 0;
339184251Smarcel
340184251Smarcel	done = 0;
341184251Smarcel	error = 0;
342184251Smarcel	timeout *= 10;
343184251Smarcel	while (!done && !error && timeout) {
344184251Smarcel		DELAY(100);
345184251Smarcel		timeout--;
346184251Smarcel
347184251Smarcel		switch (sc->sc_cmdset) {
348184251Smarcel		case CFI_VEND_INTEL_ECS:
349184251Smarcel		case CFI_VEND_INTEL_SCS:
350188156Ssam			st = cfi_read(sc, ofs);
351188156Ssam			done = (st & CFI_INTEL_STATUS_WSMS);
352184251Smarcel			if (done) {
353188156Ssam				/* NB: bit 0 is reserved */
354188156Ssam				st &= ~(CFI_INTEL_XSTATUS_RSVD |
355188156Ssam					CFI_INTEL_STATUS_WSMS |
356188156Ssam					CFI_INTEL_STATUS_RSVD);
357188156Ssam				if (st & CFI_INTEL_STATUS_DPS)
358184251Smarcel					error = EPERM;
359188156Ssam				else if (st & CFI_INTEL_STATUS_PSLBS)
360184251Smarcel					error = EIO;
361188156Ssam				else if (st & CFI_INTEL_STATUS_ECLBS)
362184251Smarcel					error = ENXIO;
363188156Ssam				else if (st)
364188156Ssam					error = EACCES;
365184251Smarcel			}
366184251Smarcel			break;
367184251Smarcel		case CFI_VEND_AMD_SCS:
368184251Smarcel		case CFI_VEND_AMD_ECS:
369188156Ssam			st0 = cfi_read(sc, ofs);
370188156Ssam			st = cfi_read(sc, ofs);
371184251Smarcel			done = ((st & 0x40) == (st0 & 0x40)) ? 1 : 0;
372184251Smarcel			break;
373184251Smarcel		}
374184251Smarcel	}
375184251Smarcel	if (!done && !error)
376184251Smarcel		error = ETIMEDOUT;
377184251Smarcel	if (error)
378188156Ssam		printf("\nerror=%d (st 0x%x st0 0x%x)\n", error, st, st0);
379184251Smarcel	return (error);
380184251Smarcel}
381184251Smarcel
382184251Smarcelint
383184251Smarcelcfi_write_block(struct cfi_softc *sc)
384184251Smarcel{
385184251Smarcel	union {
386184251Smarcel		uint8_t		*x8;
387184251Smarcel		uint16_t	*x16;
388184251Smarcel		uint32_t	*x32;
389184251Smarcel	} ptr;
390184251Smarcel	register_t intr;
391184251Smarcel	int error, i;
392184251Smarcel
393184251Smarcel	/* Erase the block. */
394184251Smarcel	switch (sc->sc_cmdset) {
395184251Smarcel	case CFI_VEND_INTEL_ECS:
396184251Smarcel	case CFI_VEND_INTEL_SCS:
397184251Smarcel		cfi_write(sc, sc->sc_wrofs, CFI_BCS_BLOCK_ERASE);
398184251Smarcel		cfi_write(sc, sc->sc_wrofs, CFI_BCS_CONFIRM);
399184251Smarcel		break;
400184251Smarcel	case CFI_VEND_AMD_SCS:
401184251Smarcel	case CFI_VEND_AMD_ECS:
402184251Smarcel		cfi_amd_write(sc, sc->sc_wrofs, AMD_ADDR_START,
403184251Smarcel		    CFI_AMD_ERASE_SECTOR);
404184251Smarcel		cfi_amd_write(sc, sc->sc_wrofs, 0, CFI_AMD_BLOCK_ERASE);
405184251Smarcel		break;
406184251Smarcel	default:
407184251Smarcel		/* Better safe than sorry... */
408184251Smarcel		return (ENODEV);
409184251Smarcel	}
410188156Ssam	error = cfi_wait_ready(sc, sc->sc_wrofs, sc->sc_erase_timeout);
411184251Smarcel	if (error)
412184251Smarcel		goto out;
413184251Smarcel
414184251Smarcel	/* Write the block. */
415184251Smarcel	ptr.x8 = sc->sc_wrbuf;
416184251Smarcel	for (i = 0; i < sc->sc_wrbufsz; i += sc->sc_width) {
417184251Smarcel
418184251Smarcel		/*
419184251Smarcel		 * Make sure the command to start a write and the
420184251Smarcel		 * actual write happens back-to-back without any
421184251Smarcel		 * excessive delays.
422184251Smarcel		 */
423184251Smarcel		intr = intr_disable();
424184251Smarcel
425184251Smarcel		switch (sc->sc_cmdset) {
426184251Smarcel		case CFI_VEND_INTEL_ECS:
427184251Smarcel		case CFI_VEND_INTEL_SCS:
428184251Smarcel			cfi_write(sc, sc->sc_wrofs + i, CFI_BCS_PROGRAM);
429184251Smarcel			break;
430184251Smarcel		case CFI_VEND_AMD_SCS:
431184251Smarcel		case CFI_VEND_AMD_ECS:
432184251Smarcel			cfi_amd_write(sc, 0, AMD_ADDR_START, CFI_AMD_PROGRAM);
433184251Smarcel			break;
434184251Smarcel		}
435184251Smarcel		switch (sc->sc_width) {
436184251Smarcel		case 1:
437184251Smarcel			bus_space_write_1(sc->sc_tag, sc->sc_handle,
438184251Smarcel			    sc->sc_wrofs + i, *(ptr.x8)++);
439184251Smarcel			break;
440184251Smarcel		case 2:
441184251Smarcel			bus_space_write_2(sc->sc_tag, sc->sc_handle,
442184251Smarcel			    sc->sc_wrofs + i, *(ptr.x16)++);
443184251Smarcel			break;
444184251Smarcel		case 4:
445184251Smarcel			bus_space_write_4(sc->sc_tag, sc->sc_handle,
446184251Smarcel			    sc->sc_wrofs + i, *(ptr.x32)++);
447184251Smarcel			break;
448184251Smarcel		}
449184251Smarcel
450184251Smarcel		intr_restore(intr);
451184251Smarcel
452188156Ssam		error = cfi_wait_ready(sc, sc->sc_wrofs, sc->sc_write_timeout);
453184251Smarcel		if (error)
454184251Smarcel			goto out;
455184251Smarcel	}
456184251Smarcel
457184251Smarcel	/* error is 0. */
458184251Smarcel
459184251Smarcel out:
460184251Smarcel	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
461184251Smarcel	return (error);
462184251Smarcel}
463188156Ssam
464188156Ssam#ifdef CFI_SUPPORT_STRATAFLASH
465188156Ssam/*
466188156Ssam * Intel StrataFlash Protection Register Support.
467188156Ssam *
468188156Ssam * The memory includes a 128-bit Protection Register that can be
469188156Ssam * used for security.  There are two 64-bit segments; one is programmed
470188156Ssam * at the factory with a unique 64-bit number which is immutable.
471188156Ssam * The other segment is left blank for User (OEM) programming.
472188267Ssam * The User/OEM segment is One Time Programmable (OTP).  It can also
473188332Ssam * be locked to prevent any further writes by setting bit 0 of the
474188267Ssam * Protection Lock Register (PLR).  The PLR can written only once.
475188156Ssam */
476188156Ssam
477188156Ssamstatic uint16_t
478188156Ssamcfi_get16(struct cfi_softc *sc, int off)
479188156Ssam{
480188156Ssam	uint16_t v = bus_space_read_2(sc->sc_tag, sc->sc_handle, off<<1);
481188156Ssam	return v;
482188156Ssam}
483188156Ssam
484188268Ssam#ifdef CFI_ARMEDANDDANGEROUS
485188156Ssamstatic void
486188156Ssamcfi_put16(struct cfi_softc *sc, int off, uint16_t v)
487188156Ssam{
488188156Ssam	bus_space_write_2(sc->sc_tag, sc->sc_handle, off<<1, v);
489188156Ssam}
490188268Ssam#endif
491188156Ssam
492188156Ssam/*
493188156Ssam * Read the factory-defined 64-bit segment of the PR.
494188156Ssam */
495188156Ssamint
496188156Ssamcfi_intel_get_factory_pr(struct cfi_softc *sc, uint64_t *id)
497188156Ssam{
498188156Ssam	if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
499188156Ssam		return EOPNOTSUPP;
500188156Ssam	KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
501188156Ssam
502188156Ssam	cfi_write(sc, 0, CFI_INTEL_READ_ID);
503188156Ssam	*id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(0)))<<48 |
504188156Ssam	      ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(1)))<<32 |
505188156Ssam	      ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(2)))<<16 |
506188156Ssam	      ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(3)));
507188156Ssam	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
508188156Ssam	return 0;
509188156Ssam}
510188156Ssam
511188156Ssam/*
512188156Ssam * Read the User/OEM 64-bit segment of the PR.
513188156Ssam */
514188156Ssamint
515188156Ssamcfi_intel_get_oem_pr(struct cfi_softc *sc, uint64_t *id)
516188156Ssam{
517188156Ssam	if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
518188156Ssam		return EOPNOTSUPP;
519188156Ssam	KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
520188156Ssam
521188156Ssam	cfi_write(sc, 0, CFI_INTEL_READ_ID);
522188156Ssam	*id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(4)))<<48 |
523188156Ssam	      ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(5)))<<32 |
524188156Ssam	      ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(6)))<<16 |
525188156Ssam	      ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(7)));
526188156Ssam	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
527188156Ssam	return 0;
528188156Ssam}
529188156Ssam
530188156Ssam/*
531188156Ssam * Write the User/OEM 64-bit segment of the PR.
532188267Ssam * XXX should allow writing individual words/bytes
533188156Ssam */
534188156Ssamint
535188156Ssamcfi_intel_set_oem_pr(struct cfi_softc *sc, uint64_t id)
536188156Ssam{
537188267Ssam#ifdef CFI_ARMEDANDDANGEROUS
538188156Ssam	register_t intr;
539188156Ssam	int i, error;
540188267Ssam#endif
541188156Ssam
542188156Ssam	if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
543188156Ssam		return EOPNOTSUPP;
544188156Ssam	KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
545188156Ssam
546188267Ssam#ifdef CFI_ARMEDANDDANGEROUS
547188156Ssam	for (i = 7; i >= 4; i--, id >>= 16) {
548188156Ssam		intr = intr_disable();
549188156Ssam		cfi_write(sc, 0, CFI_INTEL_PP_SETUP);
550188156Ssam		cfi_put16(sc, CFI_INTEL_PR(i), id&0xffff);
551188156Ssam		intr_restore(intr);
552188156Ssam		error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS,
553188156Ssam		    sc->sc_write_timeout);
554188156Ssam		if (error)
555188156Ssam			break;
556188156Ssam	}
557188156Ssam	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
558188156Ssam	return error;
559188267Ssam#else
560188267Ssam	device_printf(sc->sc_dev, "%s: OEM PR not set, "
561188267Ssam	    "CFI_ARMEDANDDANGEROUS not configured\n", __func__);
562188267Ssam	return ENXIO;
563188267Ssam#endif
564188156Ssam}
565188156Ssam
566188156Ssam/*
567188156Ssam * Read the contents of the Protection Lock Register.
568188156Ssam */
569188156Ssamint
570188156Ssamcfi_intel_get_plr(struct cfi_softc *sc, uint32_t *plr)
571188156Ssam{
572188156Ssam	if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
573188156Ssam		return EOPNOTSUPP;
574188156Ssam	KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
575188156Ssam
576188156Ssam	cfi_write(sc, 0, CFI_INTEL_READ_ID);
577188156Ssam	*plr = cfi_get16(sc, CFI_INTEL_PLR);
578188156Ssam	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
579188156Ssam	return 0;
580188156Ssam}
581188156Ssam
582188156Ssam/*
583188156Ssam * Write the Protection Lock Register to lock down the
584188156Ssam * user-settable segment of the Protection Register.
585188156Ssam * NOTE: this operation is not reversible.
586188156Ssam */
587188156Ssamint
588188156Ssamcfi_intel_set_plr(struct cfi_softc *sc)
589188156Ssam{
590188156Ssam#ifdef CFI_ARMEDANDDANGEROUS
591188156Ssam	register_t intr;
592188268Ssam	int error;
593188156Ssam#endif
594188156Ssam	if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
595188156Ssam		return EOPNOTSUPP;
596188156Ssam	KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
597188156Ssam
598188156Ssam#ifdef CFI_ARMEDANDDANGEROUS
599188156Ssam	/* worthy of console msg */
600188156Ssam	device_printf(sc->sc_dev, "set PLR\n");
601188156Ssam	intr = intr_disable();
602188156Ssam	cfi_write(sc, 0, CFI_INTEL_PP_SETUP);
603188156Ssam	cfi_put16(sc, CFI_INTEL_PLR, 0xFFFD);
604188156Ssam	intr_restore(intr);
605188156Ssam	error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, sc->sc_write_timeout);
606188156Ssam	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
607188268Ssam	return error;
608188156Ssam#else
609188156Ssam	device_printf(sc->sc_dev, "%s: PLR not set, "
610188156Ssam	    "CFI_ARMEDANDDANGEROUS not configured\n", __func__);
611188268Ssam	return ENXIO;
612188156Ssam#endif
613188156Ssam}
614188156Ssam#endif /* CFI_SUPPORT_STRATAFLASH */
615