cfi_core.c revision 189606
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 189606 2009-03-09 23:16:02Z sam $");
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>
39184251Smarcel#include <sys/kernel.h>
40184251Smarcel#include <sys/malloc.h>
41184251Smarcel#include <sys/module.h>
42184251Smarcel#include <sys/rman.h>
43184251Smarcel#include <sys/sysctl.h>
44184251Smarcel
45184251Smarcel#include <machine/bus.h>
46184251Smarcel
47184251Smarcel#include <dev/cfi/cfi_reg.h>
48184251Smarcel#include <dev/cfi/cfi_var.h>
49184251Smarcel
50184251Smarcelextern struct cdevsw cfi_cdevsw;
51184251Smarcel
52184251Smarcelchar cfi_driver_name[] = "cfi";
53184251Smarceldevclass_t cfi_devclass;
54189606Ssamdevclass_t cfi_diskclass;
55184251Smarcel
56184251Smarceluint32_t
57184251Smarcelcfi_read(struct cfi_softc *sc, u_int ofs)
58184251Smarcel{
59184251Smarcel	uint32_t val;
60184251Smarcel
61184251Smarcel	ofs &= ~(sc->sc_width - 1);
62184251Smarcel	switch (sc->sc_width) {
63184251Smarcel	case 1:
64184251Smarcel		val = bus_space_read_1(sc->sc_tag, sc->sc_handle, ofs);
65184251Smarcel		break;
66184251Smarcel	case 2:
67184251Smarcel		val = bus_space_read_2(sc->sc_tag, sc->sc_handle, ofs);
68184251Smarcel		break;
69184251Smarcel	case 4:
70184251Smarcel		val = bus_space_read_4(sc->sc_tag, sc->sc_handle, ofs);
71184251Smarcel		break;
72184251Smarcel	default:
73184251Smarcel		val = ~0;
74184251Smarcel		break;
75184251Smarcel	}
76184251Smarcel	return (val);
77184251Smarcel}
78184251Smarcel
79184251Smarcelstatic void
80184251Smarcelcfi_write(struct cfi_softc *sc, u_int ofs, u_int val)
81184251Smarcel{
82184251Smarcel
83184251Smarcel	ofs &= ~(sc->sc_width - 1);
84184251Smarcel	switch (sc->sc_width) {
85184251Smarcel	case 1:
86184251Smarcel		bus_space_write_1(sc->sc_tag, sc->sc_handle, ofs, val);
87184251Smarcel		break;
88184251Smarcel	case 2:
89184251Smarcel		bus_space_write_2(sc->sc_tag, sc->sc_handle, ofs, val);
90184251Smarcel		break;
91184251Smarcel	case 4:
92184251Smarcel		bus_space_write_4(sc->sc_tag, sc->sc_handle, ofs, val);
93184251Smarcel		break;
94184251Smarcel	}
95184251Smarcel}
96184251Smarcel
97184251Smarceluint8_t
98184251Smarcelcfi_read_qry(struct cfi_softc *sc, u_int ofs)
99184251Smarcel{
100184251Smarcel	uint8_t val;
101184251Smarcel
102184251Smarcel	cfi_write(sc, CFI_QRY_CMD_ADDR * sc->sc_width, CFI_QRY_CMD_DATA);
103184251Smarcel	val = cfi_read(sc, ofs * sc->sc_width);
104184251Smarcel	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
105184251Smarcel	return (val);
106184251Smarcel}
107184251Smarcel
108184251Smarcelstatic void
109184251Smarcelcfi_amd_write(struct cfi_softc *sc, u_int ofs, u_int addr, u_int data)
110184251Smarcel{
111184251Smarcel
112184251Smarcel	cfi_write(sc, ofs + AMD_ADDR_START, CFI_AMD_UNLOCK);
113184251Smarcel	cfi_write(sc, ofs + AMD_ADDR_ACK, CFI_AMD_UNLOCK_ACK);
114184251Smarcel	cfi_write(sc, ofs + addr, data);
115184251Smarcel}
116184251Smarcel
117184251Smarcelstatic char *
118184251Smarcelcfi_fmtsize(uint32_t sz)
119184251Smarcel{
120184251Smarcel	static char buf[8];
121184251Smarcel	static const char *sfx[] = { "", "K", "M", "G" };
122184251Smarcel	int sfxidx;
123184251Smarcel
124184251Smarcel	sfxidx = 0;
125184251Smarcel	while (sfxidx < 3 && sz > 1023) {
126184251Smarcel		sz /= 1024;
127184251Smarcel		sfxidx++;
128184251Smarcel	}
129184251Smarcel
130184251Smarcel	sprintf(buf, "%u%sB", sz, sfx[sfxidx]);
131184251Smarcel	return (buf);
132184251Smarcel}
133184251Smarcel
134184251Smarcelint
135184251Smarcelcfi_probe(device_t dev)
136184251Smarcel{
137184251Smarcel	char desc[80];
138184251Smarcel	struct cfi_softc *sc;
139184251Smarcel	char *vend_str;
140184251Smarcel	int error;
141184251Smarcel	uint16_t iface, vend;
142184251Smarcel
143184251Smarcel	sc = device_get_softc(dev);
144184251Smarcel	sc->sc_dev = dev;
145184251Smarcel
146184251Smarcel	sc->sc_rid = 0;
147184251Smarcel	sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid,
148184251Smarcel	    RF_ACTIVE);
149184251Smarcel	if (sc->sc_res == NULL)
150184251Smarcel		return (ENXIO);
151184251Smarcel
152184251Smarcel	sc->sc_tag = rman_get_bustag(sc->sc_res);
153184251Smarcel	sc->sc_handle = rman_get_bushandle(sc->sc_res);
154184251Smarcel
155188087Ssam	if (sc->sc_width == 0) {
156188087Ssam		sc->sc_width = 1;
157188087Ssam		while (sc->sc_width <= 4) {
158188087Ssam			if (cfi_read_qry(sc, CFI_QRY_IDENT) == 'Q')
159188087Ssam				break;
160188087Ssam			sc->sc_width <<= 1;
161188087Ssam		}
162188087Ssam	} else if (cfi_read_qry(sc, CFI_QRY_IDENT) != 'Q') {
163188087Ssam		error = ENXIO;
164188087Ssam		goto out;
165184251Smarcel	}
166184251Smarcel	if (sc->sc_width > 4) {
167184251Smarcel		error = ENXIO;
168184251Smarcel		goto out;
169184251Smarcel	}
170184251Smarcel
171184251Smarcel	/* We got a Q. Check if we also have the R and the Y. */
172184251Smarcel	if (cfi_read_qry(sc, CFI_QRY_IDENT + 1) != 'R' ||
173184251Smarcel	    cfi_read_qry(sc, CFI_QRY_IDENT + 2) != 'Y') {
174184251Smarcel		error = ENXIO;
175184251Smarcel		goto out;
176184251Smarcel	}
177184251Smarcel
178184251Smarcel	/* Get the vendor and command set. */
179184251Smarcel	vend = cfi_read_qry(sc, CFI_QRY_VEND) |
180184251Smarcel	    (cfi_read_qry(sc, CFI_QRY_VEND + 1) << 8);
181184251Smarcel
182184251Smarcel	sc->sc_cmdset = vend;
183184251Smarcel
184184251Smarcel	switch (vend) {
185184251Smarcel	case CFI_VEND_AMD_ECS:
186184251Smarcel	case CFI_VEND_AMD_SCS:
187184251Smarcel		vend_str = "AMD/Fujitsu";
188184251Smarcel		break;
189184251Smarcel	case CFI_VEND_INTEL_ECS:
190184251Smarcel		vend_str = "Intel/Sharp";
191184251Smarcel		break;
192184251Smarcel	case CFI_VEND_INTEL_SCS:
193184251Smarcel		vend_str = "Intel";
194184251Smarcel		break;
195184251Smarcel	case CFI_VEND_MITSUBISHI_ECS:
196184251Smarcel	case CFI_VEND_MITSUBISHI_SCS:
197184251Smarcel		vend_str = "Mitsubishi";
198184251Smarcel		break;
199184251Smarcel	default:
200184251Smarcel		vend_str = "Unknown vendor";
201184251Smarcel		break;
202184251Smarcel	}
203184251Smarcel
204184251Smarcel	/* Get the device size. */
205184251Smarcel	sc->sc_size = 1U << cfi_read_qry(sc, CFI_QRY_SIZE);
206184251Smarcel
207184251Smarcel	/* Sanity-check the I/F */
208184251Smarcel	iface = cfi_read_qry(sc, CFI_QRY_IFACE) |
209184251Smarcel	    (cfi_read_qry(sc, CFI_QRY_IFACE + 1) << 8);
210184251Smarcel
211184251Smarcel	/*
212184251Smarcel	 * Adding 1 to iface will give us a bit-wise "switch"
213184251Smarcel	 * that allows us to test for the interface width by
214184251Smarcel	 * testing a single bit.
215184251Smarcel	 */
216184251Smarcel	iface++;
217184251Smarcel
218184251Smarcel	error = (iface & sc->sc_width) ? 0 : EINVAL;
219184251Smarcel	if (error)
220184251Smarcel		goto out;
221184251Smarcel
222184251Smarcel	snprintf(desc, sizeof(desc), "%s - %s", vend_str,
223184251Smarcel	    cfi_fmtsize(sc->sc_size));
224184251Smarcel	device_set_desc_copy(dev, desc);
225184251Smarcel
226184251Smarcel out:
227184251Smarcel	bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res);
228184251Smarcel	return (error);
229184251Smarcel}
230184251Smarcel
231184251Smarcelint
232184251Smarcelcfi_attach(device_t dev)
233184251Smarcel{
234184251Smarcel	struct cfi_softc *sc;
235184251Smarcel	u_int blksz, blocks;
236184251Smarcel	u_int r, u;
237184251Smarcel
238184251Smarcel	sc = device_get_softc(dev);
239184251Smarcel	sc->sc_dev = dev;
240184251Smarcel
241184251Smarcel	sc->sc_rid = 0;
242184251Smarcel	sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid,
243184251Smarcel	    RF_ACTIVE);
244184251Smarcel	if (sc->sc_res == NULL)
245184251Smarcel		return (ENXIO);
246184251Smarcel
247184251Smarcel	sc->sc_tag = rman_get_bustag(sc->sc_res);
248184251Smarcel	sc->sc_handle = rman_get_bushandle(sc->sc_res);
249184251Smarcel
250184251Smarcel	/* Get time-out values for erase and write. */
251184251Smarcel	sc->sc_write_timeout = 1 << cfi_read_qry(sc, CFI_QRY_TTO_WRITE);
252184251Smarcel	sc->sc_erase_timeout = 1 << cfi_read_qry(sc, CFI_QRY_TTO_ERASE);
253184251Smarcel	sc->sc_write_timeout *= 1 << cfi_read_qry(sc, CFI_QRY_MTO_WRITE);
254184251Smarcel	sc->sc_erase_timeout *= 1 << cfi_read_qry(sc, CFI_QRY_MTO_ERASE);
255184251Smarcel
256184251Smarcel	/* Get erase regions. */
257184251Smarcel	sc->sc_regions = cfi_read_qry(sc, CFI_QRY_NREGIONS);
258184251Smarcel	sc->sc_region = malloc(sc->sc_regions * sizeof(struct cfi_region),
259184251Smarcel	    M_TEMP, M_WAITOK | M_ZERO);
260184251Smarcel	for (r = 0; r < sc->sc_regions; r++) {
261184251Smarcel		blocks = cfi_read_qry(sc, CFI_QRY_REGION(r)) |
262184251Smarcel		    (cfi_read_qry(sc, CFI_QRY_REGION(r) + 1) << 8);
263184251Smarcel		sc->sc_region[r].r_blocks = blocks + 1;
264184251Smarcel
265184251Smarcel		blksz = cfi_read_qry(sc, CFI_QRY_REGION(r) + 2) |
266184251Smarcel		    (cfi_read_qry(sc, CFI_QRY_REGION(r) + 3) << 8);
267184251Smarcel		sc->sc_region[r].r_blksz = (blksz == 0) ? 128 :
268184251Smarcel		    blksz * 256;
269184251Smarcel	}
270184251Smarcel
271184251Smarcel	/* Reset the device to a default state. */
272184251Smarcel	cfi_write(sc, 0, CFI_BCS_CLEAR_STATUS);
273184251Smarcel
274184251Smarcel	if (bootverbose) {
275184251Smarcel		device_printf(dev, "[");
276184251Smarcel		for (r = 0; r < sc->sc_regions; r++) {
277184251Smarcel			printf("%ux%s%s", sc->sc_region[r].r_blocks,
278184251Smarcel			    cfi_fmtsize(sc->sc_region[r].r_blksz),
279184251Smarcel			    (r == sc->sc_regions - 1) ? "]\n" : ",");
280184251Smarcel		}
281184251Smarcel	}
282184251Smarcel
283184251Smarcel	u = device_get_unit(dev);
284184251Smarcel	sc->sc_nod = make_dev(&cfi_cdevsw, u, UID_ROOT, GID_WHEEL, 0600,
285184251Smarcel	    "%s%u", cfi_driver_name, u);
286184251Smarcel	sc->sc_nod->si_drv1 = sc;
287184251Smarcel
288189606Ssam	device_add_child(dev, "cfid",
289189606Ssam	    devclass_find_free_unit(cfi_diskclass, 0));
290189606Ssam	bus_generic_attach(dev);
291189606Ssam
292184251Smarcel	return (0);
293184251Smarcel}
294184251Smarcel
295184251Smarcelint
296184251Smarcelcfi_detach(device_t dev)
297184251Smarcel{
298184251Smarcel	struct cfi_softc *sc;
299184251Smarcel
300184251Smarcel	sc = device_get_softc(dev);
301184251Smarcel
302184251Smarcel	destroy_dev(sc->sc_nod);
303184251Smarcel	free(sc->sc_region, M_TEMP);
304184251Smarcel	bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res);
305184251Smarcel	return (0);
306184251Smarcel}
307184251Smarcel
308184251Smarcelstatic int
309188156Ssamcfi_wait_ready(struct cfi_softc *sc, u_int ofs, u_int timeout)
310184251Smarcel{
311184251Smarcel	int done, error;
312188156Ssam	uint32_t st0 = 0, st = 0;
313184251Smarcel
314184251Smarcel	done = 0;
315184251Smarcel	error = 0;
316184251Smarcel	timeout *= 10;
317184251Smarcel	while (!done && !error && timeout) {
318184251Smarcel		DELAY(100);
319184251Smarcel		timeout--;
320184251Smarcel
321184251Smarcel		switch (sc->sc_cmdset) {
322184251Smarcel		case CFI_VEND_INTEL_ECS:
323184251Smarcel		case CFI_VEND_INTEL_SCS:
324188156Ssam			st = cfi_read(sc, ofs);
325188156Ssam			done = (st & CFI_INTEL_STATUS_WSMS);
326184251Smarcel			if (done) {
327188156Ssam				/* NB: bit 0 is reserved */
328188156Ssam				st &= ~(CFI_INTEL_XSTATUS_RSVD |
329188156Ssam					CFI_INTEL_STATUS_WSMS |
330188156Ssam					CFI_INTEL_STATUS_RSVD);
331188156Ssam				if (st & CFI_INTEL_STATUS_DPS)
332184251Smarcel					error = EPERM;
333188156Ssam				else if (st & CFI_INTEL_STATUS_PSLBS)
334184251Smarcel					error = EIO;
335188156Ssam				else if (st & CFI_INTEL_STATUS_ECLBS)
336184251Smarcel					error = ENXIO;
337188156Ssam				else if (st)
338188156Ssam					error = EACCES;
339184251Smarcel			}
340184251Smarcel			break;
341184251Smarcel		case CFI_VEND_AMD_SCS:
342184251Smarcel		case CFI_VEND_AMD_ECS:
343188156Ssam			st0 = cfi_read(sc, ofs);
344188156Ssam			st = cfi_read(sc, ofs);
345184251Smarcel			done = ((st & 0x40) == (st0 & 0x40)) ? 1 : 0;
346184251Smarcel			break;
347184251Smarcel		}
348184251Smarcel	}
349184251Smarcel	if (!done && !error)
350184251Smarcel		error = ETIMEDOUT;
351184251Smarcel	if (error)
352188156Ssam		printf("\nerror=%d (st 0x%x st0 0x%x)\n", error, st, st0);
353184251Smarcel	return (error);
354184251Smarcel}
355184251Smarcel
356184251Smarcelint
357184251Smarcelcfi_write_block(struct cfi_softc *sc)
358184251Smarcel{
359184251Smarcel	union {
360184251Smarcel		uint8_t		*x8;
361184251Smarcel		uint16_t	*x16;
362184251Smarcel		uint32_t	*x32;
363184251Smarcel	} ptr;
364184251Smarcel	register_t intr;
365184251Smarcel	int error, i;
366184251Smarcel
367184251Smarcel	/* Erase the block. */
368184251Smarcel	switch (sc->sc_cmdset) {
369184251Smarcel	case CFI_VEND_INTEL_ECS:
370184251Smarcel	case CFI_VEND_INTEL_SCS:
371184251Smarcel		cfi_write(sc, sc->sc_wrofs, CFI_BCS_BLOCK_ERASE);
372184251Smarcel		cfi_write(sc, sc->sc_wrofs, CFI_BCS_CONFIRM);
373184251Smarcel		break;
374184251Smarcel	case CFI_VEND_AMD_SCS:
375184251Smarcel	case CFI_VEND_AMD_ECS:
376184251Smarcel		cfi_amd_write(sc, sc->sc_wrofs, AMD_ADDR_START,
377184251Smarcel		    CFI_AMD_ERASE_SECTOR);
378184251Smarcel		cfi_amd_write(sc, sc->sc_wrofs, 0, CFI_AMD_BLOCK_ERASE);
379184251Smarcel		break;
380184251Smarcel	default:
381184251Smarcel		/* Better safe than sorry... */
382184251Smarcel		return (ENODEV);
383184251Smarcel	}
384188156Ssam	error = cfi_wait_ready(sc, sc->sc_wrofs, sc->sc_erase_timeout);
385184251Smarcel	if (error)
386184251Smarcel		goto out;
387184251Smarcel
388184251Smarcel	/* Write the block. */
389184251Smarcel	ptr.x8 = sc->sc_wrbuf;
390184251Smarcel	for (i = 0; i < sc->sc_wrbufsz; i += sc->sc_width) {
391184251Smarcel
392184251Smarcel		/*
393184251Smarcel		 * Make sure the command to start a write and the
394184251Smarcel		 * actual write happens back-to-back without any
395184251Smarcel		 * excessive delays.
396184251Smarcel		 */
397184251Smarcel		intr = intr_disable();
398184251Smarcel
399184251Smarcel		switch (sc->sc_cmdset) {
400184251Smarcel		case CFI_VEND_INTEL_ECS:
401184251Smarcel		case CFI_VEND_INTEL_SCS:
402184251Smarcel			cfi_write(sc, sc->sc_wrofs + i, CFI_BCS_PROGRAM);
403184251Smarcel			break;
404184251Smarcel		case CFI_VEND_AMD_SCS:
405184251Smarcel		case CFI_VEND_AMD_ECS:
406184251Smarcel			cfi_amd_write(sc, 0, AMD_ADDR_START, CFI_AMD_PROGRAM);
407184251Smarcel			break;
408184251Smarcel		}
409184251Smarcel		switch (sc->sc_width) {
410184251Smarcel		case 1:
411184251Smarcel			bus_space_write_1(sc->sc_tag, sc->sc_handle,
412184251Smarcel			    sc->sc_wrofs + i, *(ptr.x8)++);
413184251Smarcel			break;
414184251Smarcel		case 2:
415184251Smarcel			bus_space_write_2(sc->sc_tag, sc->sc_handle,
416184251Smarcel			    sc->sc_wrofs + i, *(ptr.x16)++);
417184251Smarcel			break;
418184251Smarcel		case 4:
419184251Smarcel			bus_space_write_4(sc->sc_tag, sc->sc_handle,
420184251Smarcel			    sc->sc_wrofs + i, *(ptr.x32)++);
421184251Smarcel			break;
422184251Smarcel		}
423184251Smarcel
424184251Smarcel		intr_restore(intr);
425184251Smarcel
426188156Ssam		error = cfi_wait_ready(sc, sc->sc_wrofs, sc->sc_write_timeout);
427184251Smarcel		if (error)
428184251Smarcel			goto out;
429184251Smarcel	}
430184251Smarcel
431184251Smarcel	/* error is 0. */
432184251Smarcel
433184251Smarcel out:
434184251Smarcel	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
435184251Smarcel	return (error);
436184251Smarcel}
437188156Ssam
438188156Ssam#ifdef CFI_SUPPORT_STRATAFLASH
439188156Ssam/*
440188156Ssam * Intel StrataFlash Protection Register Support.
441188156Ssam *
442188156Ssam * The memory includes a 128-bit Protection Register that can be
443188156Ssam * used for security.  There are two 64-bit segments; one is programmed
444188156Ssam * at the factory with a unique 64-bit number which is immutable.
445188156Ssam * The other segment is left blank for User (OEM) programming.
446188267Ssam * The User/OEM segment is One Time Programmable (OTP).  It can also
447188332Ssam * be locked to prevent any further writes by setting bit 0 of the
448188267Ssam * Protection Lock Register (PLR).  The PLR can written only once.
449188156Ssam */
450188156Ssam
451188156Ssamstatic uint16_t
452188156Ssamcfi_get16(struct cfi_softc *sc, int off)
453188156Ssam{
454188156Ssam	uint16_t v = bus_space_read_2(sc->sc_tag, sc->sc_handle, off<<1);
455188156Ssam	return v;
456188156Ssam}
457188156Ssam
458188268Ssam#ifdef CFI_ARMEDANDDANGEROUS
459188156Ssamstatic void
460188156Ssamcfi_put16(struct cfi_softc *sc, int off, uint16_t v)
461188156Ssam{
462188156Ssam	bus_space_write_2(sc->sc_tag, sc->sc_handle, off<<1, v);
463188156Ssam}
464188268Ssam#endif
465188156Ssam
466188156Ssam/*
467188156Ssam * Read the factory-defined 64-bit segment of the PR.
468188156Ssam */
469188156Ssamint
470188156Ssamcfi_intel_get_factory_pr(struct cfi_softc *sc, uint64_t *id)
471188156Ssam{
472188156Ssam	if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
473188156Ssam		return EOPNOTSUPP;
474188156Ssam	KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
475188156Ssam
476188156Ssam	cfi_write(sc, 0, CFI_INTEL_READ_ID);
477188156Ssam	*id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(0)))<<48 |
478188156Ssam	      ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(1)))<<32 |
479188156Ssam	      ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(2)))<<16 |
480188156Ssam	      ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(3)));
481188156Ssam	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
482188156Ssam	return 0;
483188156Ssam}
484188156Ssam
485188156Ssam/*
486188156Ssam * Read the User/OEM 64-bit segment of the PR.
487188156Ssam */
488188156Ssamint
489188156Ssamcfi_intel_get_oem_pr(struct cfi_softc *sc, uint64_t *id)
490188156Ssam{
491188156Ssam	if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
492188156Ssam		return EOPNOTSUPP;
493188156Ssam	KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
494188156Ssam
495188156Ssam	cfi_write(sc, 0, CFI_INTEL_READ_ID);
496188156Ssam	*id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(4)))<<48 |
497188156Ssam	      ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(5)))<<32 |
498188156Ssam	      ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(6)))<<16 |
499188156Ssam	      ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(7)));
500188156Ssam	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
501188156Ssam	return 0;
502188156Ssam}
503188156Ssam
504188156Ssam/*
505188156Ssam * Write the User/OEM 64-bit segment of the PR.
506188267Ssam * XXX should allow writing individual words/bytes
507188156Ssam */
508188156Ssamint
509188156Ssamcfi_intel_set_oem_pr(struct cfi_softc *sc, uint64_t id)
510188156Ssam{
511188267Ssam#ifdef CFI_ARMEDANDDANGEROUS
512188156Ssam	register_t intr;
513188156Ssam	int i, error;
514188267Ssam#endif
515188156Ssam
516188156Ssam	if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
517188156Ssam		return EOPNOTSUPP;
518188156Ssam	KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
519188156Ssam
520188267Ssam#ifdef CFI_ARMEDANDDANGEROUS
521188156Ssam	for (i = 7; i >= 4; i--, id >>= 16) {
522188156Ssam		intr = intr_disable();
523188156Ssam		cfi_write(sc, 0, CFI_INTEL_PP_SETUP);
524188156Ssam		cfi_put16(sc, CFI_INTEL_PR(i), id&0xffff);
525188156Ssam		intr_restore(intr);
526188156Ssam		error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS,
527188156Ssam		    sc->sc_write_timeout);
528188156Ssam		if (error)
529188156Ssam			break;
530188156Ssam	}
531188156Ssam	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
532188156Ssam	return error;
533188267Ssam#else
534188267Ssam	device_printf(sc->sc_dev, "%s: OEM PR not set, "
535188267Ssam	    "CFI_ARMEDANDDANGEROUS not configured\n", __func__);
536188267Ssam	return ENXIO;
537188267Ssam#endif
538188156Ssam}
539188156Ssam
540188156Ssam/*
541188156Ssam * Read the contents of the Protection Lock Register.
542188156Ssam */
543188156Ssamint
544188156Ssamcfi_intel_get_plr(struct cfi_softc *sc, uint32_t *plr)
545188156Ssam{
546188156Ssam	if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
547188156Ssam		return EOPNOTSUPP;
548188156Ssam	KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
549188156Ssam
550188156Ssam	cfi_write(sc, 0, CFI_INTEL_READ_ID);
551188156Ssam	*plr = cfi_get16(sc, CFI_INTEL_PLR);
552188156Ssam	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
553188156Ssam	return 0;
554188156Ssam}
555188156Ssam
556188156Ssam/*
557188156Ssam * Write the Protection Lock Register to lock down the
558188156Ssam * user-settable segment of the Protection Register.
559188156Ssam * NOTE: this operation is not reversible.
560188156Ssam */
561188156Ssamint
562188156Ssamcfi_intel_set_plr(struct cfi_softc *sc)
563188156Ssam{
564188156Ssam#ifdef CFI_ARMEDANDDANGEROUS
565188156Ssam	register_t intr;
566188268Ssam	int error;
567188156Ssam#endif
568188156Ssam	if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
569188156Ssam		return EOPNOTSUPP;
570188156Ssam	KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
571188156Ssam
572188156Ssam#ifdef CFI_ARMEDANDDANGEROUS
573188156Ssam	/* worthy of console msg */
574188156Ssam	device_printf(sc->sc_dev, "set PLR\n");
575188156Ssam	intr = intr_disable();
576188156Ssam	cfi_write(sc, 0, CFI_INTEL_PP_SETUP);
577188156Ssam	cfi_put16(sc, CFI_INTEL_PLR, 0xFFFD);
578188156Ssam	intr_restore(intr);
579188156Ssam	error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, sc->sc_write_timeout);
580188156Ssam	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
581188268Ssam	return error;
582188156Ssam#else
583188156Ssam	device_printf(sc->sc_dev, "%s: PLR not set, "
584188156Ssam	    "CFI_ARMEDANDDANGEROUS not configured\n", __func__);
585188268Ssam	return ENXIO;
586188156Ssam#endif
587188156Ssam}
588188156Ssam#endif /* CFI_SUPPORT_STRATAFLASH */
589