cfi_core.c revision 193936
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 193936 2009-06-10 17:41:24Z imp $");
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
288193936Simp	device_add_child(dev, "cfid", -1);
289189606Ssam	bus_generic_attach(dev);
290189606Ssam
291184251Smarcel	return (0);
292184251Smarcel}
293184251Smarcel
294184251Smarcelint
295184251Smarcelcfi_detach(device_t dev)
296184251Smarcel{
297184251Smarcel	struct cfi_softc *sc;
298184251Smarcel
299184251Smarcel	sc = device_get_softc(dev);
300184251Smarcel
301184251Smarcel	destroy_dev(sc->sc_nod);
302184251Smarcel	free(sc->sc_region, M_TEMP);
303184251Smarcel	bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res);
304184251Smarcel	return (0);
305184251Smarcel}
306184251Smarcel
307184251Smarcelstatic int
308188156Ssamcfi_wait_ready(struct cfi_softc *sc, u_int ofs, u_int timeout)
309184251Smarcel{
310184251Smarcel	int done, error;
311188156Ssam	uint32_t st0 = 0, st = 0;
312184251Smarcel
313184251Smarcel	done = 0;
314184251Smarcel	error = 0;
315184251Smarcel	timeout *= 10;
316184251Smarcel	while (!done && !error && timeout) {
317184251Smarcel		DELAY(100);
318184251Smarcel		timeout--;
319184251Smarcel
320184251Smarcel		switch (sc->sc_cmdset) {
321184251Smarcel		case CFI_VEND_INTEL_ECS:
322184251Smarcel		case CFI_VEND_INTEL_SCS:
323188156Ssam			st = cfi_read(sc, ofs);
324188156Ssam			done = (st & CFI_INTEL_STATUS_WSMS);
325184251Smarcel			if (done) {
326188156Ssam				/* NB: bit 0 is reserved */
327188156Ssam				st &= ~(CFI_INTEL_XSTATUS_RSVD |
328188156Ssam					CFI_INTEL_STATUS_WSMS |
329188156Ssam					CFI_INTEL_STATUS_RSVD);
330188156Ssam				if (st & CFI_INTEL_STATUS_DPS)
331184251Smarcel					error = EPERM;
332188156Ssam				else if (st & CFI_INTEL_STATUS_PSLBS)
333184251Smarcel					error = EIO;
334188156Ssam				else if (st & CFI_INTEL_STATUS_ECLBS)
335184251Smarcel					error = ENXIO;
336188156Ssam				else if (st)
337188156Ssam					error = EACCES;
338184251Smarcel			}
339184251Smarcel			break;
340184251Smarcel		case CFI_VEND_AMD_SCS:
341184251Smarcel		case CFI_VEND_AMD_ECS:
342188156Ssam			st0 = cfi_read(sc, ofs);
343188156Ssam			st = cfi_read(sc, ofs);
344184251Smarcel			done = ((st & 0x40) == (st0 & 0x40)) ? 1 : 0;
345184251Smarcel			break;
346184251Smarcel		}
347184251Smarcel	}
348184251Smarcel	if (!done && !error)
349184251Smarcel		error = ETIMEDOUT;
350184251Smarcel	if (error)
351188156Ssam		printf("\nerror=%d (st 0x%x st0 0x%x)\n", error, st, st0);
352184251Smarcel	return (error);
353184251Smarcel}
354184251Smarcel
355184251Smarcelint
356184251Smarcelcfi_write_block(struct cfi_softc *sc)
357184251Smarcel{
358184251Smarcel	union {
359184251Smarcel		uint8_t		*x8;
360184251Smarcel		uint16_t	*x16;
361184251Smarcel		uint32_t	*x32;
362184251Smarcel	} ptr;
363184251Smarcel	register_t intr;
364184251Smarcel	int error, i;
365184251Smarcel
366184251Smarcel	/* Erase the block. */
367184251Smarcel	switch (sc->sc_cmdset) {
368184251Smarcel	case CFI_VEND_INTEL_ECS:
369184251Smarcel	case CFI_VEND_INTEL_SCS:
370184251Smarcel		cfi_write(sc, sc->sc_wrofs, CFI_BCS_BLOCK_ERASE);
371184251Smarcel		cfi_write(sc, sc->sc_wrofs, CFI_BCS_CONFIRM);
372184251Smarcel		break;
373184251Smarcel	case CFI_VEND_AMD_SCS:
374184251Smarcel	case CFI_VEND_AMD_ECS:
375184251Smarcel		cfi_amd_write(sc, sc->sc_wrofs, AMD_ADDR_START,
376184251Smarcel		    CFI_AMD_ERASE_SECTOR);
377184251Smarcel		cfi_amd_write(sc, sc->sc_wrofs, 0, CFI_AMD_BLOCK_ERASE);
378184251Smarcel		break;
379184251Smarcel	default:
380184251Smarcel		/* Better safe than sorry... */
381184251Smarcel		return (ENODEV);
382184251Smarcel	}
383188156Ssam	error = cfi_wait_ready(sc, sc->sc_wrofs, sc->sc_erase_timeout);
384184251Smarcel	if (error)
385184251Smarcel		goto out;
386184251Smarcel
387184251Smarcel	/* Write the block. */
388184251Smarcel	ptr.x8 = sc->sc_wrbuf;
389184251Smarcel	for (i = 0; i < sc->sc_wrbufsz; i += sc->sc_width) {
390184251Smarcel
391184251Smarcel		/*
392184251Smarcel		 * Make sure the command to start a write and the
393184251Smarcel		 * actual write happens back-to-back without any
394184251Smarcel		 * excessive delays.
395184251Smarcel		 */
396184251Smarcel		intr = intr_disable();
397184251Smarcel
398184251Smarcel		switch (sc->sc_cmdset) {
399184251Smarcel		case CFI_VEND_INTEL_ECS:
400184251Smarcel		case CFI_VEND_INTEL_SCS:
401184251Smarcel			cfi_write(sc, sc->sc_wrofs + i, CFI_BCS_PROGRAM);
402184251Smarcel			break;
403184251Smarcel		case CFI_VEND_AMD_SCS:
404184251Smarcel		case CFI_VEND_AMD_ECS:
405184251Smarcel			cfi_amd_write(sc, 0, AMD_ADDR_START, CFI_AMD_PROGRAM);
406184251Smarcel			break;
407184251Smarcel		}
408184251Smarcel		switch (sc->sc_width) {
409184251Smarcel		case 1:
410184251Smarcel			bus_space_write_1(sc->sc_tag, sc->sc_handle,
411184251Smarcel			    sc->sc_wrofs + i, *(ptr.x8)++);
412184251Smarcel			break;
413184251Smarcel		case 2:
414184251Smarcel			bus_space_write_2(sc->sc_tag, sc->sc_handle,
415184251Smarcel			    sc->sc_wrofs + i, *(ptr.x16)++);
416184251Smarcel			break;
417184251Smarcel		case 4:
418184251Smarcel			bus_space_write_4(sc->sc_tag, sc->sc_handle,
419184251Smarcel			    sc->sc_wrofs + i, *(ptr.x32)++);
420184251Smarcel			break;
421184251Smarcel		}
422184251Smarcel
423184251Smarcel		intr_restore(intr);
424184251Smarcel
425188156Ssam		error = cfi_wait_ready(sc, sc->sc_wrofs, sc->sc_write_timeout);
426184251Smarcel		if (error)
427184251Smarcel			goto out;
428184251Smarcel	}
429184251Smarcel
430184251Smarcel	/* error is 0. */
431184251Smarcel
432184251Smarcel out:
433184251Smarcel	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
434184251Smarcel	return (error);
435184251Smarcel}
436188156Ssam
437188156Ssam#ifdef CFI_SUPPORT_STRATAFLASH
438188156Ssam/*
439188156Ssam * Intel StrataFlash Protection Register Support.
440188156Ssam *
441188156Ssam * The memory includes a 128-bit Protection Register that can be
442188156Ssam * used for security.  There are two 64-bit segments; one is programmed
443188156Ssam * at the factory with a unique 64-bit number which is immutable.
444188156Ssam * The other segment is left blank for User (OEM) programming.
445188267Ssam * The User/OEM segment is One Time Programmable (OTP).  It can also
446188332Ssam * be locked to prevent any further writes by setting bit 0 of the
447188267Ssam * Protection Lock Register (PLR).  The PLR can written only once.
448188156Ssam */
449188156Ssam
450188156Ssamstatic uint16_t
451188156Ssamcfi_get16(struct cfi_softc *sc, int off)
452188156Ssam{
453188156Ssam	uint16_t v = bus_space_read_2(sc->sc_tag, sc->sc_handle, off<<1);
454188156Ssam	return v;
455188156Ssam}
456188156Ssam
457188268Ssam#ifdef CFI_ARMEDANDDANGEROUS
458188156Ssamstatic void
459188156Ssamcfi_put16(struct cfi_softc *sc, int off, uint16_t v)
460188156Ssam{
461188156Ssam	bus_space_write_2(sc->sc_tag, sc->sc_handle, off<<1, v);
462188156Ssam}
463188268Ssam#endif
464188156Ssam
465188156Ssam/*
466188156Ssam * Read the factory-defined 64-bit segment of the PR.
467188156Ssam */
468188156Ssamint
469188156Ssamcfi_intel_get_factory_pr(struct cfi_softc *sc, uint64_t *id)
470188156Ssam{
471188156Ssam	if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
472188156Ssam		return EOPNOTSUPP;
473188156Ssam	KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
474188156Ssam
475188156Ssam	cfi_write(sc, 0, CFI_INTEL_READ_ID);
476188156Ssam	*id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(0)))<<48 |
477188156Ssam	      ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(1)))<<32 |
478188156Ssam	      ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(2)))<<16 |
479188156Ssam	      ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(3)));
480188156Ssam	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
481188156Ssam	return 0;
482188156Ssam}
483188156Ssam
484188156Ssam/*
485188156Ssam * Read the User/OEM 64-bit segment of the PR.
486188156Ssam */
487188156Ssamint
488188156Ssamcfi_intel_get_oem_pr(struct cfi_softc *sc, uint64_t *id)
489188156Ssam{
490188156Ssam	if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
491188156Ssam		return EOPNOTSUPP;
492188156Ssam	KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
493188156Ssam
494188156Ssam	cfi_write(sc, 0, CFI_INTEL_READ_ID);
495188156Ssam	*id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(4)))<<48 |
496188156Ssam	      ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(5)))<<32 |
497188156Ssam	      ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(6)))<<16 |
498188156Ssam	      ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(7)));
499188156Ssam	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
500188156Ssam	return 0;
501188156Ssam}
502188156Ssam
503188156Ssam/*
504188156Ssam * Write the User/OEM 64-bit segment of the PR.
505188267Ssam * XXX should allow writing individual words/bytes
506188156Ssam */
507188156Ssamint
508188156Ssamcfi_intel_set_oem_pr(struct cfi_softc *sc, uint64_t id)
509188156Ssam{
510188267Ssam#ifdef CFI_ARMEDANDDANGEROUS
511188156Ssam	register_t intr;
512188156Ssam	int i, error;
513188267Ssam#endif
514188156Ssam
515188156Ssam	if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
516188156Ssam		return EOPNOTSUPP;
517188156Ssam	KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
518188156Ssam
519188267Ssam#ifdef CFI_ARMEDANDDANGEROUS
520188156Ssam	for (i = 7; i >= 4; i--, id >>= 16) {
521188156Ssam		intr = intr_disable();
522188156Ssam		cfi_write(sc, 0, CFI_INTEL_PP_SETUP);
523188156Ssam		cfi_put16(sc, CFI_INTEL_PR(i), id&0xffff);
524188156Ssam		intr_restore(intr);
525188156Ssam		error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS,
526188156Ssam		    sc->sc_write_timeout);
527188156Ssam		if (error)
528188156Ssam			break;
529188156Ssam	}
530188156Ssam	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
531188156Ssam	return error;
532188267Ssam#else
533188267Ssam	device_printf(sc->sc_dev, "%s: OEM PR not set, "
534188267Ssam	    "CFI_ARMEDANDDANGEROUS not configured\n", __func__);
535188267Ssam	return ENXIO;
536188267Ssam#endif
537188156Ssam}
538188156Ssam
539188156Ssam/*
540188156Ssam * Read the contents of the Protection Lock Register.
541188156Ssam */
542188156Ssamint
543188156Ssamcfi_intel_get_plr(struct cfi_softc *sc, uint32_t *plr)
544188156Ssam{
545188156Ssam	if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
546188156Ssam		return EOPNOTSUPP;
547188156Ssam	KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
548188156Ssam
549188156Ssam	cfi_write(sc, 0, CFI_INTEL_READ_ID);
550188156Ssam	*plr = cfi_get16(sc, CFI_INTEL_PLR);
551188156Ssam	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
552188156Ssam	return 0;
553188156Ssam}
554188156Ssam
555188156Ssam/*
556188156Ssam * Write the Protection Lock Register to lock down the
557188156Ssam * user-settable segment of the Protection Register.
558188156Ssam * NOTE: this operation is not reversible.
559188156Ssam */
560188156Ssamint
561188156Ssamcfi_intel_set_plr(struct cfi_softc *sc)
562188156Ssam{
563188156Ssam#ifdef CFI_ARMEDANDDANGEROUS
564188156Ssam	register_t intr;
565188268Ssam	int error;
566188156Ssam#endif
567188156Ssam	if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
568188156Ssam		return EOPNOTSUPP;
569188156Ssam	KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
570188156Ssam
571188156Ssam#ifdef CFI_ARMEDANDDANGEROUS
572188156Ssam	/* worthy of console msg */
573188156Ssam	device_printf(sc->sc_dev, "set PLR\n");
574188156Ssam	intr = intr_disable();
575188156Ssam	cfi_write(sc, 0, CFI_INTEL_PP_SETUP);
576188156Ssam	cfi_put16(sc, CFI_INTEL_PLR, 0xFFFD);
577188156Ssam	intr_restore(intr);
578188156Ssam	error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, sc->sc_write_timeout);
579188156Ssam	cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
580188268Ssam	return error;
581188156Ssam#else
582188156Ssam	device_printf(sc->sc_dev, "%s: PLR not set, "
583188156Ssam	    "CFI_ARMEDANDDANGEROUS not configured\n", __func__);
584188268Ssam	return ENXIO;
585188156Ssam#endif
586188156Ssam}
587188156Ssam#endif /* CFI_SUPPORT_STRATAFLASH */
588