1e00251b7SMarcel Moolenaar /*-
27282444bSPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause
37282444bSPedro F. Giffuni *
4e00251b7SMarcel Moolenaar * Copyright (c) 2007, Juniper Networks, Inc.
5f4358134SBrooks Davis * Copyright (c) 2012-2013, SRI International
6e00251b7SMarcel Moolenaar * All rights reserved.
7e00251b7SMarcel Moolenaar *
8f4358134SBrooks Davis * Portions of this software were developed by SRI International and the
9f4358134SBrooks Davis * University of Cambridge Computer Laboratory under DARPA/AFRL contract
10f4358134SBrooks Davis * (FA8750-10-C-0237) ("CTSRD"), as part of the DARPA CRASH research
11f4358134SBrooks Davis * programme.
12f4358134SBrooks Davis *
13e00251b7SMarcel Moolenaar * Redistribution and use in source and binary forms, with or without
14e00251b7SMarcel Moolenaar * modification, are permitted provided that the following conditions
15e00251b7SMarcel Moolenaar * are met:
16e00251b7SMarcel Moolenaar * 1. Redistributions of source code must retain the above copyright
17e00251b7SMarcel Moolenaar * notice, this list of conditions and the following disclaimer.
18e00251b7SMarcel Moolenaar * 2. Redistributions in binary form must reproduce the above copyright
19e00251b7SMarcel Moolenaar * notice, this list of conditions and the following disclaimer in the
20e00251b7SMarcel Moolenaar * documentation and/or other materials provided with the distribution.
21e00251b7SMarcel Moolenaar * 3. Neither the name of the author nor the names of any co-contributors
22e00251b7SMarcel Moolenaar * may be used to endorse or promote products derived from this software
23e00251b7SMarcel Moolenaar * without specific prior written permission.
24e00251b7SMarcel Moolenaar *
25e00251b7SMarcel Moolenaar * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26e00251b7SMarcel Moolenaar * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27e00251b7SMarcel Moolenaar * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28e00251b7SMarcel Moolenaar * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
29e00251b7SMarcel Moolenaar * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30e00251b7SMarcel Moolenaar * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31e00251b7SMarcel Moolenaar * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
32e00251b7SMarcel Moolenaar * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33e00251b7SMarcel Moolenaar * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34e00251b7SMarcel Moolenaar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35e00251b7SMarcel Moolenaar * SUCH DAMAGE.
36e00251b7SMarcel Moolenaar */
37e00251b7SMarcel Moolenaar
38e00251b7SMarcel Moolenaar #include <sys/cdefs.h>
3963425a7fSSam Leffler #include "opt_cfi.h"
4063425a7fSSam Leffler
41e00251b7SMarcel Moolenaar #include <sys/param.h>
42e00251b7SMarcel Moolenaar #include <sys/systm.h>
43e00251b7SMarcel Moolenaar #include <sys/bus.h>
44e00251b7SMarcel Moolenaar #include <sys/conf.h>
459c1f1330SJayachandran C. #include <sys/endian.h>
46c88b210bSBrooks Davis #include <sys/kenv.h>
47e00251b7SMarcel Moolenaar #include <sys/kernel.h>
48e00251b7SMarcel Moolenaar #include <sys/malloc.h>
49e00251b7SMarcel Moolenaar #include <sys/module.h>
50e00251b7SMarcel Moolenaar #include <sys/rman.h>
51e00251b7SMarcel Moolenaar #include <sys/sysctl.h>
52e00251b7SMarcel Moolenaar
53e00251b7SMarcel Moolenaar #include <machine/bus.h>
54e00251b7SMarcel Moolenaar
55e00251b7SMarcel Moolenaar #include <dev/cfi/cfi_reg.h>
56e00251b7SMarcel Moolenaar #include <dev/cfi/cfi_var.h>
57e00251b7SMarcel Moolenaar
58f4358134SBrooks Davis static void cfi_add_sysctls(struct cfi_softc *);
59f4358134SBrooks Davis
60e00251b7SMarcel Moolenaar extern struct cdevsw cfi_cdevsw;
61e00251b7SMarcel Moolenaar
62e00251b7SMarcel Moolenaar char cfi_driver_name[] = "cfi";
63e00251b7SMarcel Moolenaar
64e00251b7SMarcel Moolenaar uint32_t
cfi_read_raw(struct cfi_softc * sc,u_int ofs)659c1f1330SJayachandran C. cfi_read_raw(struct cfi_softc *sc, u_int ofs)
66e00251b7SMarcel Moolenaar {
67e00251b7SMarcel Moolenaar uint32_t val;
68e00251b7SMarcel Moolenaar
69e00251b7SMarcel Moolenaar ofs &= ~(sc->sc_width - 1);
70e00251b7SMarcel Moolenaar switch (sc->sc_width) {
71e00251b7SMarcel Moolenaar case 1:
72e00251b7SMarcel Moolenaar val = bus_space_read_1(sc->sc_tag, sc->sc_handle, ofs);
73e00251b7SMarcel Moolenaar break;
74e00251b7SMarcel Moolenaar case 2:
75e00251b7SMarcel Moolenaar val = bus_space_read_2(sc->sc_tag, sc->sc_handle, ofs);
76e00251b7SMarcel Moolenaar break;
77e00251b7SMarcel Moolenaar case 4:
78e00251b7SMarcel Moolenaar val = bus_space_read_4(sc->sc_tag, sc->sc_handle, ofs);
79e00251b7SMarcel Moolenaar break;
80e00251b7SMarcel Moolenaar default:
81e00251b7SMarcel Moolenaar val = ~0;
82e00251b7SMarcel Moolenaar break;
83e00251b7SMarcel Moolenaar }
84e00251b7SMarcel Moolenaar return (val);
85e00251b7SMarcel Moolenaar }
86e00251b7SMarcel Moolenaar
879c1f1330SJayachandran C. uint32_t
cfi_read(struct cfi_softc * sc,u_int ofs)889c1f1330SJayachandran C. cfi_read(struct cfi_softc *sc, u_int ofs)
899c1f1330SJayachandran C. {
909c1f1330SJayachandran C. uint32_t val;
919c1f1330SJayachandran C. uint16_t sval;
929c1f1330SJayachandran C.
939c1f1330SJayachandran C. ofs &= ~(sc->sc_width - 1);
949c1f1330SJayachandran C. switch (sc->sc_width) {
959c1f1330SJayachandran C. case 1:
969c1f1330SJayachandran C. val = bus_space_read_1(sc->sc_tag, sc->sc_handle, ofs);
979c1f1330SJayachandran C. break;
989c1f1330SJayachandran C. case 2:
999c1f1330SJayachandran C. sval = bus_space_read_2(sc->sc_tag, sc->sc_handle, ofs);
1003f84dfc1SAdrian Chadd #ifdef CFI_HARDWAREBYTESWAP
1013f84dfc1SAdrian Chadd val = sval;
1023f84dfc1SAdrian Chadd #else
1039c1f1330SJayachandran C. val = le16toh(sval);
1043f84dfc1SAdrian Chadd #endif
1059c1f1330SJayachandran C. break;
1069c1f1330SJayachandran C. case 4:
1079c1f1330SJayachandran C. val = bus_space_read_4(sc->sc_tag, sc->sc_handle, ofs);
1083f84dfc1SAdrian Chadd #ifndef CFI_HARDWAREBYTESWAP
1099c1f1330SJayachandran C. val = le32toh(val);
1103f84dfc1SAdrian Chadd #endif
1119c1f1330SJayachandran C. break;
1129c1f1330SJayachandran C. default:
1139c1f1330SJayachandran C. val = ~0;
1149c1f1330SJayachandran C. break;
1159c1f1330SJayachandran C. }
1169c1f1330SJayachandran C. return (val);
1179c1f1330SJayachandran C. }
1189c1f1330SJayachandran C.
119e00251b7SMarcel Moolenaar static void
cfi_write(struct cfi_softc * sc,u_int ofs,u_int val)120e00251b7SMarcel Moolenaar cfi_write(struct cfi_softc *sc, u_int ofs, u_int val)
121e00251b7SMarcel Moolenaar {
122e00251b7SMarcel Moolenaar
123e00251b7SMarcel Moolenaar ofs &= ~(sc->sc_width - 1);
124e00251b7SMarcel Moolenaar switch (sc->sc_width) {
125e00251b7SMarcel Moolenaar case 1:
126e00251b7SMarcel Moolenaar bus_space_write_1(sc->sc_tag, sc->sc_handle, ofs, val);
127e00251b7SMarcel Moolenaar break;
128e00251b7SMarcel Moolenaar case 2:
1293f84dfc1SAdrian Chadd #ifdef CFI_HARDWAREBYTESWAP
1303f84dfc1SAdrian Chadd bus_space_write_2(sc->sc_tag, sc->sc_handle, ofs, val);
1313f84dfc1SAdrian Chadd #else
1329c1f1330SJayachandran C. bus_space_write_2(sc->sc_tag, sc->sc_handle, ofs, htole16(val));
1333f84dfc1SAdrian Chadd
1343f84dfc1SAdrian Chadd #endif
135e00251b7SMarcel Moolenaar break;
136e00251b7SMarcel Moolenaar case 4:
1373f84dfc1SAdrian Chadd #ifdef CFI_HARDWAREBYTESWAP
1383f84dfc1SAdrian Chadd bus_space_write_4(sc->sc_tag, sc->sc_handle, ofs, val);
1393f84dfc1SAdrian Chadd #else
1409c1f1330SJayachandran C. bus_space_write_4(sc->sc_tag, sc->sc_handle, ofs, htole32(val));
1413f84dfc1SAdrian Chadd #endif
142e00251b7SMarcel Moolenaar break;
143e00251b7SMarcel Moolenaar }
144e00251b7SMarcel Moolenaar }
145e00251b7SMarcel Moolenaar
1466bf13692SAdrian Chadd /*
1476bf13692SAdrian Chadd * This is same workaound as NetBSD sys/dev/nor/cfi.c cfi_reset_default()
1486bf13692SAdrian Chadd */
1496bf13692SAdrian Chadd static void
cfi_reset_default(struct cfi_softc * sc)1506bf13692SAdrian Chadd cfi_reset_default(struct cfi_softc *sc)
1516bf13692SAdrian Chadd {
1526bf13692SAdrian Chadd
1536bf13692SAdrian Chadd cfi_write(sc, 0, CFI_BCS_READ_ARRAY2);
1546bf13692SAdrian Chadd cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
1556bf13692SAdrian Chadd }
1566bf13692SAdrian Chadd
157e00251b7SMarcel Moolenaar uint8_t
cfi_read_qry(struct cfi_softc * sc,u_int ofs)158e00251b7SMarcel Moolenaar cfi_read_qry(struct cfi_softc *sc, u_int ofs)
159e00251b7SMarcel Moolenaar {
160e00251b7SMarcel Moolenaar uint8_t val;
161e00251b7SMarcel Moolenaar
162e00251b7SMarcel Moolenaar cfi_write(sc, CFI_QRY_CMD_ADDR * sc->sc_width, CFI_QRY_CMD_DATA);
163e00251b7SMarcel Moolenaar val = cfi_read(sc, ofs * sc->sc_width);
1646bf13692SAdrian Chadd cfi_reset_default(sc);
165e00251b7SMarcel Moolenaar return (val);
166e00251b7SMarcel Moolenaar }
167e00251b7SMarcel Moolenaar
168e00251b7SMarcel Moolenaar static void
cfi_amd_write(struct cfi_softc * sc,u_int ofs,u_int addr,u_int data)169e00251b7SMarcel Moolenaar cfi_amd_write(struct cfi_softc *sc, u_int ofs, u_int addr, u_int data)
170e00251b7SMarcel Moolenaar {
171e00251b7SMarcel Moolenaar
172e00251b7SMarcel Moolenaar cfi_write(sc, ofs + AMD_ADDR_START, CFI_AMD_UNLOCK);
173e00251b7SMarcel Moolenaar cfi_write(sc, ofs + AMD_ADDR_ACK, CFI_AMD_UNLOCK_ACK);
174e00251b7SMarcel Moolenaar cfi_write(sc, ofs + addr, data);
175e00251b7SMarcel Moolenaar }
176e00251b7SMarcel Moolenaar
177e00251b7SMarcel Moolenaar static char *
cfi_fmtsize(uint32_t sz)178e00251b7SMarcel Moolenaar cfi_fmtsize(uint32_t sz)
179e00251b7SMarcel Moolenaar {
180e00251b7SMarcel Moolenaar static char buf[8];
181e00251b7SMarcel Moolenaar static const char *sfx[] = { "", "K", "M", "G" };
182e00251b7SMarcel Moolenaar int sfxidx;
183e00251b7SMarcel Moolenaar
184e00251b7SMarcel Moolenaar sfxidx = 0;
185e00251b7SMarcel Moolenaar while (sfxidx < 3 && sz > 1023) {
186e00251b7SMarcel Moolenaar sz /= 1024;
187e00251b7SMarcel Moolenaar sfxidx++;
188e00251b7SMarcel Moolenaar }
189e00251b7SMarcel Moolenaar
190e00251b7SMarcel Moolenaar sprintf(buf, "%u%sB", sz, sfx[sfxidx]);
191e00251b7SMarcel Moolenaar return (buf);
192e00251b7SMarcel Moolenaar }
193e00251b7SMarcel Moolenaar
194e00251b7SMarcel Moolenaar int
cfi_probe(device_t dev)195e00251b7SMarcel Moolenaar cfi_probe(device_t dev)
196e00251b7SMarcel Moolenaar {
197e00251b7SMarcel Moolenaar struct cfi_softc *sc;
198e00251b7SMarcel Moolenaar char *vend_str;
199e00251b7SMarcel Moolenaar int error;
200e00251b7SMarcel Moolenaar uint16_t iface, vend;
201e00251b7SMarcel Moolenaar
202e00251b7SMarcel Moolenaar sc = device_get_softc(dev);
203e00251b7SMarcel Moolenaar sc->sc_dev = dev;
204e00251b7SMarcel Moolenaar
205e00251b7SMarcel Moolenaar sc->sc_rid = 0;
206e00251b7SMarcel Moolenaar sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid,
207e00251b7SMarcel Moolenaar RF_ACTIVE);
208e00251b7SMarcel Moolenaar if (sc->sc_res == NULL)
209e00251b7SMarcel Moolenaar return (ENXIO);
210e00251b7SMarcel Moolenaar
211e00251b7SMarcel Moolenaar sc->sc_tag = rman_get_bustag(sc->sc_res);
212e00251b7SMarcel Moolenaar sc->sc_handle = rman_get_bushandle(sc->sc_res);
213e00251b7SMarcel Moolenaar
214e73090b5SSam Leffler if (sc->sc_width == 0) {
215e00251b7SMarcel Moolenaar sc->sc_width = 1;
216e00251b7SMarcel Moolenaar while (sc->sc_width <= 4) {
217e00251b7SMarcel Moolenaar if (cfi_read_qry(sc, CFI_QRY_IDENT) == 'Q')
218e00251b7SMarcel Moolenaar break;
219e00251b7SMarcel Moolenaar sc->sc_width <<= 1;
220e00251b7SMarcel Moolenaar }
221e73090b5SSam Leffler } else if (cfi_read_qry(sc, CFI_QRY_IDENT) != 'Q') {
222e73090b5SSam Leffler error = ENXIO;
223e73090b5SSam Leffler goto out;
224e73090b5SSam Leffler }
225e00251b7SMarcel Moolenaar if (sc->sc_width > 4) {
226e00251b7SMarcel Moolenaar error = ENXIO;
227e00251b7SMarcel Moolenaar goto out;
228e00251b7SMarcel Moolenaar }
229e00251b7SMarcel Moolenaar
230e00251b7SMarcel Moolenaar /* We got a Q. Check if we also have the R and the Y. */
231e00251b7SMarcel Moolenaar if (cfi_read_qry(sc, CFI_QRY_IDENT + 1) != 'R' ||
232e00251b7SMarcel Moolenaar cfi_read_qry(sc, CFI_QRY_IDENT + 2) != 'Y') {
233e00251b7SMarcel Moolenaar error = ENXIO;
234e00251b7SMarcel Moolenaar goto out;
235e00251b7SMarcel Moolenaar }
236e00251b7SMarcel Moolenaar
237e00251b7SMarcel Moolenaar /* Get the vendor and command set. */
238e00251b7SMarcel Moolenaar vend = cfi_read_qry(sc, CFI_QRY_VEND) |
239e00251b7SMarcel Moolenaar (cfi_read_qry(sc, CFI_QRY_VEND + 1) << 8);
240e00251b7SMarcel Moolenaar
241e00251b7SMarcel Moolenaar sc->sc_cmdset = vend;
242e00251b7SMarcel Moolenaar
243e00251b7SMarcel Moolenaar switch (vend) {
244e00251b7SMarcel Moolenaar case CFI_VEND_AMD_ECS:
245e00251b7SMarcel Moolenaar case CFI_VEND_AMD_SCS:
246e00251b7SMarcel Moolenaar vend_str = "AMD/Fujitsu";
247e00251b7SMarcel Moolenaar break;
248e00251b7SMarcel Moolenaar case CFI_VEND_INTEL_ECS:
249e00251b7SMarcel Moolenaar vend_str = "Intel/Sharp";
250e00251b7SMarcel Moolenaar break;
251e00251b7SMarcel Moolenaar case CFI_VEND_INTEL_SCS:
252e00251b7SMarcel Moolenaar vend_str = "Intel";
253e00251b7SMarcel Moolenaar break;
254e00251b7SMarcel Moolenaar case CFI_VEND_MITSUBISHI_ECS:
255e00251b7SMarcel Moolenaar case CFI_VEND_MITSUBISHI_SCS:
256e00251b7SMarcel Moolenaar vend_str = "Mitsubishi";
257e00251b7SMarcel Moolenaar break;
258e00251b7SMarcel Moolenaar default:
259e00251b7SMarcel Moolenaar vend_str = "Unknown vendor";
260e00251b7SMarcel Moolenaar break;
261e00251b7SMarcel Moolenaar }
262e00251b7SMarcel Moolenaar
263e00251b7SMarcel Moolenaar /* Get the device size. */
264e00251b7SMarcel Moolenaar sc->sc_size = 1U << cfi_read_qry(sc, CFI_QRY_SIZE);
265e00251b7SMarcel Moolenaar
266e00251b7SMarcel Moolenaar /* Sanity-check the I/F */
267e00251b7SMarcel Moolenaar iface = cfi_read_qry(sc, CFI_QRY_IFACE) |
268e00251b7SMarcel Moolenaar (cfi_read_qry(sc, CFI_QRY_IFACE + 1) << 8);
269e00251b7SMarcel Moolenaar
270e00251b7SMarcel Moolenaar /*
271e00251b7SMarcel Moolenaar * Adding 1 to iface will give us a bit-wise "switch"
272e00251b7SMarcel Moolenaar * that allows us to test for the interface width by
273e00251b7SMarcel Moolenaar * testing a single bit.
274e00251b7SMarcel Moolenaar */
275e00251b7SMarcel Moolenaar iface++;
276e00251b7SMarcel Moolenaar
277e00251b7SMarcel Moolenaar error = (iface & sc->sc_width) ? 0 : EINVAL;
278e00251b7SMarcel Moolenaar if (error)
279e00251b7SMarcel Moolenaar goto out;
280e00251b7SMarcel Moolenaar
2812ef1f61dSMark Johnston device_set_descf(dev, "%s - %s", vend_str, cfi_fmtsize(sc->sc_size));
282e00251b7SMarcel Moolenaar
283e00251b7SMarcel Moolenaar out:
284e00251b7SMarcel Moolenaar bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res);
285e00251b7SMarcel Moolenaar return (error);
286e00251b7SMarcel Moolenaar }
287e00251b7SMarcel Moolenaar
288e00251b7SMarcel Moolenaar int
cfi_attach(device_t dev)289e00251b7SMarcel Moolenaar cfi_attach(device_t dev)
290e00251b7SMarcel Moolenaar {
291e00251b7SMarcel Moolenaar struct cfi_softc *sc;
292e00251b7SMarcel Moolenaar u_int blksz, blocks;
293e00251b7SMarcel Moolenaar u_int r, u;
294f4358134SBrooks Davis uint64_t mtoexp, ttoexp;
295c88b210bSBrooks Davis #ifdef CFI_SUPPORT_STRATAFLASH
296c88b210bSBrooks Davis uint64_t ppr;
297c88b210bSBrooks Davis char name[KENV_MNAMELEN], value[32];
298c88b210bSBrooks Davis #endif
299e00251b7SMarcel Moolenaar
300e00251b7SMarcel Moolenaar sc = device_get_softc(dev);
301e00251b7SMarcel Moolenaar sc->sc_dev = dev;
302e00251b7SMarcel Moolenaar
303e00251b7SMarcel Moolenaar sc->sc_rid = 0;
304e00251b7SMarcel Moolenaar sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid,
3051a440eb2SBrooks Davis #ifndef ATSE_CFI_HACK
306e00251b7SMarcel Moolenaar RF_ACTIVE);
3071a440eb2SBrooks Davis #else
3081a440eb2SBrooks Davis RF_ACTIVE | RF_SHAREABLE);
3091a440eb2SBrooks Davis #endif
310e00251b7SMarcel Moolenaar if (sc->sc_res == NULL)
311e00251b7SMarcel Moolenaar return (ENXIO);
312e00251b7SMarcel Moolenaar
313e00251b7SMarcel Moolenaar sc->sc_tag = rman_get_bustag(sc->sc_res);
314e00251b7SMarcel Moolenaar sc->sc_handle = rman_get_bushandle(sc->sc_res);
315e00251b7SMarcel Moolenaar
316f4358134SBrooks Davis /* Get time-out values for erase, write, and buffer write. */
317f4358134SBrooks Davis ttoexp = cfi_read_qry(sc, CFI_QRY_TTO_ERASE);
318f4358134SBrooks Davis mtoexp = cfi_read_qry(sc, CFI_QRY_MTO_ERASE);
319f4358134SBrooks Davis if (ttoexp == 0) {
320f4358134SBrooks Davis device_printf(dev, "erase timeout == 0, using 2^16ms\n");
321f4358134SBrooks Davis ttoexp = 16;
322f4358134SBrooks Davis }
323f4358134SBrooks Davis if (ttoexp > 41) {
324f4358134SBrooks Davis device_printf(dev, "insane timeout: 2^%jdms\n", ttoexp);
325f4358134SBrooks Davis return (EINVAL);
326f4358134SBrooks Davis }
327f4358134SBrooks Davis if (mtoexp == 0) {
328f4358134SBrooks Davis device_printf(dev, "max erase timeout == 0, using 2^%jdms\n",
329f4358134SBrooks Davis ttoexp + 4);
330f4358134SBrooks Davis mtoexp = 4;
331f4358134SBrooks Davis }
332f4358134SBrooks Davis if (ttoexp + mtoexp > 41) {
333f4358134SBrooks Davis device_printf(dev, "insane max erase timeout: 2^%jd\n",
334f4358134SBrooks Davis ttoexp + mtoexp);
335f4358134SBrooks Davis return (EINVAL);
336f4358134SBrooks Davis }
337f4358134SBrooks Davis sc->sc_typical_timeouts[CFI_TIMEOUT_ERASE] = SBT_1MS * (1ULL << ttoexp);
338f4358134SBrooks Davis sc->sc_max_timeouts[CFI_TIMEOUT_ERASE] =
339f4358134SBrooks Davis sc->sc_typical_timeouts[CFI_TIMEOUT_ERASE] * (1ULL << mtoexp);
340f4358134SBrooks Davis
341f4358134SBrooks Davis ttoexp = cfi_read_qry(sc, CFI_QRY_TTO_WRITE);
342f4358134SBrooks Davis mtoexp = cfi_read_qry(sc, CFI_QRY_MTO_WRITE);
343f4358134SBrooks Davis if (ttoexp == 0) {
344f4358134SBrooks Davis device_printf(dev, "write timeout == 0, using 2^18ns\n");
345f4358134SBrooks Davis ttoexp = 18;
346f4358134SBrooks Davis }
347f4358134SBrooks Davis if (ttoexp > 51) {
348f4358134SBrooks Davis device_printf(dev, "insane write timeout: 2^%jdus\n", ttoexp);
349f4358134SBrooks Davis return (EINVAL);
350f4358134SBrooks Davis }
351f4358134SBrooks Davis if (mtoexp == 0) {
352f4358134SBrooks Davis device_printf(dev, "max write timeout == 0, using 2^%jdms\n",
353f4358134SBrooks Davis ttoexp + 4);
354f4358134SBrooks Davis mtoexp = 4;
355f4358134SBrooks Davis }
356f4358134SBrooks Davis if (ttoexp + mtoexp > 51) {
357f4358134SBrooks Davis device_printf(dev, "insane max write timeout: 2^%jdus\n",
358f4358134SBrooks Davis ttoexp + mtoexp);
359f4358134SBrooks Davis return (EINVAL);
360f4358134SBrooks Davis }
361f4358134SBrooks Davis sc->sc_typical_timeouts[CFI_TIMEOUT_WRITE] = SBT_1US * (1ULL << ttoexp);
362f4358134SBrooks Davis sc->sc_max_timeouts[CFI_TIMEOUT_WRITE] =
363f4358134SBrooks Davis sc->sc_typical_timeouts[CFI_TIMEOUT_WRITE] * (1ULL << mtoexp);
364f4358134SBrooks Davis
365f4358134SBrooks Davis ttoexp = cfi_read_qry(sc, CFI_QRY_TTO_BUFWRITE);
366f4358134SBrooks Davis mtoexp = cfi_read_qry(sc, CFI_QRY_MTO_BUFWRITE);
367f4358134SBrooks Davis /* Don't check for 0, it means not-supported. */
368f4358134SBrooks Davis if (ttoexp > 51) {
369f4358134SBrooks Davis device_printf(dev, "insane write timeout: 2^%jdus\n", ttoexp);
370f4358134SBrooks Davis return (EINVAL);
371f4358134SBrooks Davis }
372f4358134SBrooks Davis if (ttoexp + mtoexp > 51) {
373f4358134SBrooks Davis device_printf(dev, "insane max write timeout: 2^%jdus\n",
374f4358134SBrooks Davis ttoexp + mtoexp);
375f4358134SBrooks Davis return (EINVAL);
376f4358134SBrooks Davis }
377f4358134SBrooks Davis sc->sc_typical_timeouts[CFI_TIMEOUT_BUFWRITE] =
378f4358134SBrooks Davis SBT_1US * (1ULL << cfi_read_qry(sc, CFI_QRY_TTO_BUFWRITE));
379f4358134SBrooks Davis sc->sc_max_timeouts[CFI_TIMEOUT_BUFWRITE] =
380f4358134SBrooks Davis sc->sc_typical_timeouts[CFI_TIMEOUT_BUFWRITE] *
381f4358134SBrooks Davis (1ULL << cfi_read_qry(sc, CFI_QRY_MTO_BUFWRITE));
382f4358134SBrooks Davis
383f4358134SBrooks Davis /* Get the maximum size of a multibyte program */
384f4358134SBrooks Davis if (sc->sc_typical_timeouts[CFI_TIMEOUT_BUFWRITE] != 0)
385f4358134SBrooks Davis sc->sc_maxbuf = 1 << (cfi_read_qry(sc, CFI_QRY_MAXBUF) |
386f4358134SBrooks Davis cfi_read_qry(sc, CFI_QRY_MAXBUF) << 8);
387f4358134SBrooks Davis else
388f4358134SBrooks Davis sc->sc_maxbuf = 0;
389e00251b7SMarcel Moolenaar
390e00251b7SMarcel Moolenaar /* Get erase regions. */
391e00251b7SMarcel Moolenaar sc->sc_regions = cfi_read_qry(sc, CFI_QRY_NREGIONS);
392e00251b7SMarcel Moolenaar sc->sc_region = malloc(sc->sc_regions * sizeof(struct cfi_region),
393e00251b7SMarcel Moolenaar M_TEMP, M_WAITOK | M_ZERO);
394e00251b7SMarcel Moolenaar for (r = 0; r < sc->sc_regions; r++) {
395e00251b7SMarcel Moolenaar blocks = cfi_read_qry(sc, CFI_QRY_REGION(r)) |
396e00251b7SMarcel Moolenaar (cfi_read_qry(sc, CFI_QRY_REGION(r) + 1) << 8);
397e00251b7SMarcel Moolenaar sc->sc_region[r].r_blocks = blocks + 1;
398e00251b7SMarcel Moolenaar
399e00251b7SMarcel Moolenaar blksz = cfi_read_qry(sc, CFI_QRY_REGION(r) + 2) |
400e00251b7SMarcel Moolenaar (cfi_read_qry(sc, CFI_QRY_REGION(r) + 3) << 8);
401e00251b7SMarcel Moolenaar sc->sc_region[r].r_blksz = (blksz == 0) ? 128 :
402e00251b7SMarcel Moolenaar blksz * 256;
403e00251b7SMarcel Moolenaar }
404e00251b7SMarcel Moolenaar
405e00251b7SMarcel Moolenaar /* Reset the device to a default state. */
406e00251b7SMarcel Moolenaar cfi_write(sc, 0, CFI_BCS_CLEAR_STATUS);
407e00251b7SMarcel Moolenaar
408e00251b7SMarcel Moolenaar if (bootverbose) {
409e00251b7SMarcel Moolenaar device_printf(dev, "[");
410e00251b7SMarcel Moolenaar for (r = 0; r < sc->sc_regions; r++) {
411e00251b7SMarcel Moolenaar printf("%ux%s%s", sc->sc_region[r].r_blocks,
412e00251b7SMarcel Moolenaar cfi_fmtsize(sc->sc_region[r].r_blksz),
413e00251b7SMarcel Moolenaar (r == sc->sc_regions - 1) ? "]\n" : ",");
414e00251b7SMarcel Moolenaar }
415e00251b7SMarcel Moolenaar }
416e00251b7SMarcel Moolenaar
41791dfbef4SAllan Jude if (sc->sc_cmdset == CFI_VEND_AMD_ECS ||
41891dfbef4SAllan Jude sc->sc_cmdset == CFI_VEND_AMD_SCS) {
41991dfbef4SAllan Jude cfi_amd_write(sc, 0, AMD_ADDR_START, CFI_AMD_AUTO_SELECT);
42091dfbef4SAllan Jude sc->sc_manid = cfi_read(sc, 0);
42191dfbef4SAllan Jude sc->sc_devid = cfi_read(sc, 2);
42291dfbef4SAllan Jude device_printf(dev, "Manufacturer ID:%x Device ID:%x\n",
42391dfbef4SAllan Jude sc->sc_manid, sc->sc_devid);
42491dfbef4SAllan Jude cfi_write(sc, 0, CFI_BCS_READ_ARRAY2);
42591dfbef4SAllan Jude }
42691dfbef4SAllan Jude
427e00251b7SMarcel Moolenaar u = device_get_unit(dev);
428e00251b7SMarcel Moolenaar sc->sc_nod = make_dev(&cfi_cdevsw, u, UID_ROOT, GID_WHEEL, 0600,
429e00251b7SMarcel Moolenaar "%s%u", cfi_driver_name, u);
430e00251b7SMarcel Moolenaar sc->sc_nod->si_drv1 = sc;
431e00251b7SMarcel Moolenaar
432f4358134SBrooks Davis cfi_add_sysctls(sc);
433f4358134SBrooks Davis
434c88b210bSBrooks Davis #ifdef CFI_SUPPORT_STRATAFLASH
435c88b210bSBrooks Davis /*
436c88b210bSBrooks Davis * Store the Intel factory PPR in the environment. In some
437c88b210bSBrooks Davis * cases it is the most unique ID on a board.
438c88b210bSBrooks Davis */
439c88b210bSBrooks Davis if (cfi_intel_get_factory_pr(sc, &ppr) == 0) {
440c88b210bSBrooks Davis if (snprintf(name, sizeof(name), "%s.factory_ppr",
441c88b210bSBrooks Davis device_get_nameunit(dev)) < (sizeof(name) - 1) &&
442c88b210bSBrooks Davis snprintf(value, sizeof(value), "0x%016jx", ppr) <
443c88b210bSBrooks Davis (sizeof(value) - 1))
4442be111bfSDavide Italiano (void) kern_setenv(name, value);
445c88b210bSBrooks Davis }
446c88b210bSBrooks Davis #endif
447c88b210bSBrooks Davis
4485b56413dSWarner Losh device_add_child(dev, "cfid", DEVICE_UNIT_ANY);
449*18250ec6SJohn Baldwin bus_attach_children(dev);
450f2c6781bSSam Leffler
451e00251b7SMarcel Moolenaar return (0);
452e00251b7SMarcel Moolenaar }
453e00251b7SMarcel Moolenaar
454f4358134SBrooks Davis static void
cfi_add_sysctls(struct cfi_softc * sc)455f4358134SBrooks Davis cfi_add_sysctls(struct cfi_softc *sc)
456f4358134SBrooks Davis {
457f4358134SBrooks Davis struct sysctl_ctx_list *ctx;
458f4358134SBrooks Davis struct sysctl_oid_list *children;
459f4358134SBrooks Davis
460f4358134SBrooks Davis ctx = device_get_sysctl_ctx(sc->sc_dev);
461f4358134SBrooks Davis children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev));
462f4358134SBrooks Davis
463f4358134SBrooks Davis SYSCTL_ADD_UINT(ctx, children, OID_AUTO,
464f4358134SBrooks Davis "typical_erase_timout_count",
465f4358134SBrooks Davis CTLFLAG_RD, &sc->sc_tto_counts[CFI_TIMEOUT_ERASE],
466f4358134SBrooks Davis 0, "Number of times the typical erase timeout was exceeded");
467f4358134SBrooks Davis SYSCTL_ADD_UINT(ctx, children, OID_AUTO,
468f4358134SBrooks Davis "max_erase_timout_count",
469f4358134SBrooks Davis CTLFLAG_RD, &sc->sc_mto_counts[CFI_TIMEOUT_ERASE], 0,
470f4358134SBrooks Davis "Number of times the maximum erase timeout was exceeded");
471f4358134SBrooks Davis SYSCTL_ADD_UINT(ctx, children, OID_AUTO,
472f4358134SBrooks Davis "typical_write_timout_count",
473f4358134SBrooks Davis CTLFLAG_RD, &sc->sc_tto_counts[CFI_TIMEOUT_WRITE], 0,
474f4358134SBrooks Davis "Number of times the typical write timeout was exceeded");
475f4358134SBrooks Davis SYSCTL_ADD_UINT(ctx, children, OID_AUTO,
476f4358134SBrooks Davis "max_write_timout_count",
477f4358134SBrooks Davis CTLFLAG_RD, &sc->sc_mto_counts[CFI_TIMEOUT_WRITE], 0,
478f4358134SBrooks Davis "Number of times the maximum write timeout was exceeded");
479f4358134SBrooks Davis if (sc->sc_maxbuf > 0) {
480f4358134SBrooks Davis SYSCTL_ADD_UINT(ctx, children, OID_AUTO,
481f4358134SBrooks Davis "typical_bufwrite_timout_count",
482f4358134SBrooks Davis CTLFLAG_RD, &sc->sc_tto_counts[CFI_TIMEOUT_BUFWRITE], 0,
483f4358134SBrooks Davis "Number of times the typical buffered write timeout was "
484f4358134SBrooks Davis "exceeded");
485f4358134SBrooks Davis SYSCTL_ADD_UINT(ctx, children, OID_AUTO,
486f4358134SBrooks Davis "max_bufwrite_timout_count",
487f4358134SBrooks Davis CTLFLAG_RD, &sc->sc_mto_counts[CFI_TIMEOUT_BUFWRITE], 0,
488f4358134SBrooks Davis "Number of times the maximum buffered write timeout was "
489f4358134SBrooks Davis "exceeded");
490f4358134SBrooks Davis }
491f4358134SBrooks Davis }
492f4358134SBrooks Davis
493e00251b7SMarcel Moolenaar int
cfi_detach(device_t dev)494e00251b7SMarcel Moolenaar cfi_detach(device_t dev)
495e00251b7SMarcel Moolenaar {
496e00251b7SMarcel Moolenaar struct cfi_softc *sc;
497e00251b7SMarcel Moolenaar
498e00251b7SMarcel Moolenaar sc = device_get_softc(dev);
499e00251b7SMarcel Moolenaar
500e00251b7SMarcel Moolenaar destroy_dev(sc->sc_nod);
501e00251b7SMarcel Moolenaar free(sc->sc_region, M_TEMP);
502e00251b7SMarcel Moolenaar bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res);
503e00251b7SMarcel Moolenaar return (0);
504e00251b7SMarcel Moolenaar }
505e00251b7SMarcel Moolenaar
50691dfbef4SAllan Jude static bool
cfi_check_erase(struct cfi_softc * sc,u_int ofs,u_int sz)50791dfbef4SAllan Jude cfi_check_erase(struct cfi_softc *sc, u_int ofs, u_int sz)
50891dfbef4SAllan Jude {
50991dfbef4SAllan Jude bool result;
51091dfbef4SAllan Jude int i;
51191dfbef4SAllan Jude uint32_t val;
51291dfbef4SAllan Jude
51391dfbef4SAllan Jude result = FALSE;
51491dfbef4SAllan Jude for (i = 0; i < sz; i += sc->sc_width) {
51591dfbef4SAllan Jude val = cfi_read(sc, ofs + i);
51691dfbef4SAllan Jude switch (sc->sc_width) {
51791dfbef4SAllan Jude case 1:
51891dfbef4SAllan Jude if (val != 0xff)
51991dfbef4SAllan Jude goto out;
52091dfbef4SAllan Jude continue;
52191dfbef4SAllan Jude case 2:
52291dfbef4SAllan Jude if (val != 0xffff)
52391dfbef4SAllan Jude goto out;
52491dfbef4SAllan Jude continue;
52591dfbef4SAllan Jude case 4:
52691dfbef4SAllan Jude if (val != 0xffffffff)
52791dfbef4SAllan Jude goto out;
52891dfbef4SAllan Jude continue;
52991dfbef4SAllan Jude }
53091dfbef4SAllan Jude }
53191dfbef4SAllan Jude result = TRUE;
53291dfbef4SAllan Jude
53391dfbef4SAllan Jude out:
53491dfbef4SAllan Jude return (result);
53591dfbef4SAllan Jude }
53691dfbef4SAllan Jude
537e00251b7SMarcel Moolenaar static int
cfi_wait_ready(struct cfi_softc * sc,u_int ofs,sbintime_t start,enum cfi_wait_cmd cmd)538f4358134SBrooks Davis cfi_wait_ready(struct cfi_softc *sc, u_int ofs, sbintime_t start,
539f4358134SBrooks Davis enum cfi_wait_cmd cmd)
540e00251b7SMarcel Moolenaar {
541f4358134SBrooks Davis int done, error, tto_exceeded;
54263425a7fSSam Leffler uint32_t st0 = 0, st = 0;
543f4358134SBrooks Davis sbintime_t now;
544e00251b7SMarcel Moolenaar
545e00251b7SMarcel Moolenaar done = 0;
546e00251b7SMarcel Moolenaar error = 0;
547f4358134SBrooks Davis tto_exceeded = 0;
548f4358134SBrooks Davis while (!done && !error) {
549f4358134SBrooks Davis /*
550f4358134SBrooks Davis * Save time before we start so we always do one check
551f4358134SBrooks Davis * after the timeout has expired.
552f4358134SBrooks Davis */
553f4358134SBrooks Davis now = sbinuptime();
554e00251b7SMarcel Moolenaar
555e00251b7SMarcel Moolenaar switch (sc->sc_cmdset) {
556e00251b7SMarcel Moolenaar case CFI_VEND_INTEL_ECS:
557e00251b7SMarcel Moolenaar case CFI_VEND_INTEL_SCS:
55863425a7fSSam Leffler st = cfi_read(sc, ofs);
55963425a7fSSam Leffler done = (st & CFI_INTEL_STATUS_WSMS);
560e00251b7SMarcel Moolenaar if (done) {
56163425a7fSSam Leffler /* NB: bit 0 is reserved */
56263425a7fSSam Leffler st &= ~(CFI_INTEL_XSTATUS_RSVD |
56363425a7fSSam Leffler CFI_INTEL_STATUS_WSMS |
56463425a7fSSam Leffler CFI_INTEL_STATUS_RSVD);
56563425a7fSSam Leffler if (st & CFI_INTEL_STATUS_DPS)
566e00251b7SMarcel Moolenaar error = EPERM;
56763425a7fSSam Leffler else if (st & CFI_INTEL_STATUS_PSLBS)
568e00251b7SMarcel Moolenaar error = EIO;
56963425a7fSSam Leffler else if (st & CFI_INTEL_STATUS_ECLBS)
570e00251b7SMarcel Moolenaar error = ENXIO;
57163425a7fSSam Leffler else if (st)
57263425a7fSSam Leffler error = EACCES;
573e00251b7SMarcel Moolenaar }
574e00251b7SMarcel Moolenaar break;
575e00251b7SMarcel Moolenaar case CFI_VEND_AMD_SCS:
576e00251b7SMarcel Moolenaar case CFI_VEND_AMD_ECS:
57763425a7fSSam Leffler st0 = cfi_read(sc, ofs);
57863425a7fSSam Leffler st = cfi_read(sc, ofs);
579e00251b7SMarcel Moolenaar done = ((st & 0x40) == (st0 & 0x40)) ? 1 : 0;
580e00251b7SMarcel Moolenaar break;
581e00251b7SMarcel Moolenaar }
582f4358134SBrooks Davis
583f4358134SBrooks Davis if (tto_exceeded ||
584f4358134SBrooks Davis now > start + sc->sc_typical_timeouts[cmd]) {
585f4358134SBrooks Davis if (!tto_exceeded) {
586f4358134SBrooks Davis tto_exceeded = 1;
587f4358134SBrooks Davis sc->sc_tto_counts[cmd]++;
588f4358134SBrooks Davis #ifdef CFI_DEBUG_TIMEOUT
589f4358134SBrooks Davis device_printf(sc->sc_dev,
590f4358134SBrooks Davis "typical timeout exceeded (cmd %d)", cmd);
591f4358134SBrooks Davis #endif
592f4358134SBrooks Davis }
593f4358134SBrooks Davis if (now > start + sc->sc_max_timeouts[cmd]) {
594f4358134SBrooks Davis sc->sc_mto_counts[cmd]++;
595f4358134SBrooks Davis #ifdef CFI_DEBUG_TIMEOUT
596f4358134SBrooks Davis device_printf(sc->sc_dev,
597f4358134SBrooks Davis "max timeout exceeded (cmd %d)", cmd);
598f4358134SBrooks Davis #endif
599f4358134SBrooks Davis }
600f4358134SBrooks Davis }
601e00251b7SMarcel Moolenaar }
602e00251b7SMarcel Moolenaar if (!done && !error)
603e00251b7SMarcel Moolenaar error = ETIMEDOUT;
604e00251b7SMarcel Moolenaar if (error)
60563425a7fSSam Leffler printf("\nerror=%d (st 0x%x st0 0x%x)\n", error, st, st0);
606e00251b7SMarcel Moolenaar return (error);
607e00251b7SMarcel Moolenaar }
608e00251b7SMarcel Moolenaar
609e00251b7SMarcel Moolenaar int
cfi_write_block(struct cfi_softc * sc)610e00251b7SMarcel Moolenaar cfi_write_block(struct cfi_softc *sc)
611e00251b7SMarcel Moolenaar {
612e00251b7SMarcel Moolenaar union {
613e00251b7SMarcel Moolenaar uint8_t *x8;
614e00251b7SMarcel Moolenaar uint16_t *x16;
615e00251b7SMarcel Moolenaar uint32_t *x32;
616f4358134SBrooks Davis } ptr, cpyprt;
617e00251b7SMarcel Moolenaar register_t intr;
61891dfbef4SAllan Jude int error, i, j, neederase = 0;
619f4358134SBrooks Davis uint32_t st;
620f4358134SBrooks Davis u_int wlen;
621f4358134SBrooks Davis sbintime_t start;
62291dfbef4SAllan Jude u_int minsz;
62391dfbef4SAllan Jude uint32_t val;
624e00251b7SMarcel Moolenaar
6255faf6ff4SBrooks Davis /* Intel flash must be unlocked before modification */
6265faf6ff4SBrooks Davis switch (sc->sc_cmdset) {
6275faf6ff4SBrooks Davis case CFI_VEND_INTEL_ECS:
6285faf6ff4SBrooks Davis case CFI_VEND_INTEL_SCS:
6295faf6ff4SBrooks Davis cfi_write(sc, sc->sc_wrofs, CFI_INTEL_LBS);
6305faf6ff4SBrooks Davis cfi_write(sc, sc->sc_wrofs, CFI_INTEL_UB);
6315faf6ff4SBrooks Davis cfi_write(sc, sc->sc_wrofs, CFI_BCS_READ_ARRAY);
6325faf6ff4SBrooks Davis break;
6335faf6ff4SBrooks Davis }
6345faf6ff4SBrooks Davis
635f4358134SBrooks Davis /* Check if an erase is required. */
636f4358134SBrooks Davis for (i = 0; i < sc->sc_wrbufsz; i++)
637f4358134SBrooks Davis if ((sc->sc_wrbuf[i] & sc->sc_wrbufcpy[i]) != sc->sc_wrbuf[i]) {
638f4358134SBrooks Davis neederase = 1;
639f4358134SBrooks Davis break;
640f4358134SBrooks Davis }
641f4358134SBrooks Davis
642f4358134SBrooks Davis if (neederase) {
643f4358134SBrooks Davis intr = intr_disable();
644f4358134SBrooks Davis start = sbinuptime();
645e00251b7SMarcel Moolenaar /* Erase the block. */
646e00251b7SMarcel Moolenaar switch (sc->sc_cmdset) {
647e00251b7SMarcel Moolenaar case CFI_VEND_INTEL_ECS:
648e00251b7SMarcel Moolenaar case CFI_VEND_INTEL_SCS:
649e00251b7SMarcel Moolenaar cfi_write(sc, sc->sc_wrofs, CFI_BCS_BLOCK_ERASE);
650e00251b7SMarcel Moolenaar cfi_write(sc, sc->sc_wrofs, CFI_BCS_CONFIRM);
651e00251b7SMarcel Moolenaar break;
652e00251b7SMarcel Moolenaar case CFI_VEND_AMD_SCS:
653e00251b7SMarcel Moolenaar case CFI_VEND_AMD_ECS:
65491dfbef4SAllan Jude /* find minimum sector size */
65591dfbef4SAllan Jude minsz = sc->sc_region[0].r_blksz;
65691dfbef4SAllan Jude for (i = 1; i < sc->sc_regions; i++) {
65791dfbef4SAllan Jude if (sc->sc_region[i].r_blksz < minsz)
65891dfbef4SAllan Jude minsz = sc->sc_region[i].r_blksz;
65991dfbef4SAllan Jude }
660e00251b7SMarcel Moolenaar cfi_amd_write(sc, sc->sc_wrofs, AMD_ADDR_START,
661e00251b7SMarcel Moolenaar CFI_AMD_ERASE_SECTOR);
66291dfbef4SAllan Jude cfi_amd_write(sc, sc->sc_wrofs,
66391dfbef4SAllan Jude sc->sc_wrofs >> (ffs(minsz) - 1),
66491dfbef4SAllan Jude CFI_AMD_BLOCK_ERASE);
66591dfbef4SAllan Jude for (i = 0; i < CFI_AMD_MAXCHK; ++i) {
66691dfbef4SAllan Jude if (cfi_check_erase(sc, sc->sc_wrofs,
66791dfbef4SAllan Jude sc->sc_wrbufsz))
66891dfbef4SAllan Jude break;
66991dfbef4SAllan Jude DELAY(10);
67091dfbef4SAllan Jude }
67191dfbef4SAllan Jude if (i == CFI_AMD_MAXCHK) {
67291dfbef4SAllan Jude printf("\nCFI Sector Erase time out error\n");
67391dfbef4SAllan Jude return (ENODEV);
67491dfbef4SAllan Jude }
675e00251b7SMarcel Moolenaar break;
676e00251b7SMarcel Moolenaar default:
677e00251b7SMarcel Moolenaar /* Better safe than sorry... */
678f4358134SBrooks Davis intr_restore(intr);
679e00251b7SMarcel Moolenaar return (ENODEV);
680e00251b7SMarcel Moolenaar }
681f4358134SBrooks Davis intr_restore(intr);
682f4358134SBrooks Davis error = cfi_wait_ready(sc, sc->sc_wrofs, start,
683f4358134SBrooks Davis CFI_TIMEOUT_ERASE);
684e00251b7SMarcel Moolenaar if (error)
685e00251b7SMarcel Moolenaar goto out;
686f4358134SBrooks Davis } else
687f4358134SBrooks Davis error = 0;
688e00251b7SMarcel Moolenaar
689f4358134SBrooks Davis /* Write the block using a multibyte write if supported. */
690e00251b7SMarcel Moolenaar ptr.x8 = sc->sc_wrbuf;
691f4358134SBrooks Davis cpyprt.x8 = sc->sc_wrbufcpy;
692f4358134SBrooks Davis if (sc->sc_maxbuf > sc->sc_width) {
693f4358134SBrooks Davis switch (sc->sc_cmdset) {
694f4358134SBrooks Davis case CFI_VEND_INTEL_ECS:
695f4358134SBrooks Davis case CFI_VEND_INTEL_SCS:
696f4358134SBrooks Davis for (i = 0; i < sc->sc_wrbufsz; i += wlen) {
697f4358134SBrooks Davis wlen = MIN(sc->sc_maxbuf, sc->sc_wrbufsz - i);
698f4358134SBrooks Davis
699f4358134SBrooks Davis intr = intr_disable();
700f4358134SBrooks Davis
701f4358134SBrooks Davis start = sbinuptime();
702f4358134SBrooks Davis do {
703f4358134SBrooks Davis cfi_write(sc, sc->sc_wrofs + i,
704f4358134SBrooks Davis CFI_BCS_BUF_PROG_SETUP);
705f4358134SBrooks Davis if (sbinuptime() > start + sc->sc_max_timeouts[CFI_TIMEOUT_BUFWRITE]) {
706f4358134SBrooks Davis error = ETIMEDOUT;
707f4358134SBrooks Davis goto out;
708f4358134SBrooks Davis }
709f4358134SBrooks Davis st = cfi_read(sc, sc->sc_wrofs + i);
710f4358134SBrooks Davis } while (! (st & CFI_INTEL_STATUS_WSMS));
711f4358134SBrooks Davis
712f4358134SBrooks Davis cfi_write(sc, sc->sc_wrofs + i,
713f4358134SBrooks Davis (wlen / sc->sc_width) - 1);
714f4358134SBrooks Davis switch (sc->sc_width) {
715f4358134SBrooks Davis case 1:
716f4358134SBrooks Davis bus_space_write_region_1(sc->sc_tag,
717f4358134SBrooks Davis sc->sc_handle, sc->sc_wrofs + i,
718f4358134SBrooks Davis ptr.x8 + i, wlen);
719f4358134SBrooks Davis break;
720f4358134SBrooks Davis case 2:
721f4358134SBrooks Davis bus_space_write_region_2(sc->sc_tag,
722f4358134SBrooks Davis sc->sc_handle, sc->sc_wrofs + i,
723f4358134SBrooks Davis ptr.x16 + i / 2, wlen / 2);
724f4358134SBrooks Davis break;
725f4358134SBrooks Davis case 4:
726f4358134SBrooks Davis bus_space_write_region_4(sc->sc_tag,
727f4358134SBrooks Davis sc->sc_handle, sc->sc_wrofs + i,
728f4358134SBrooks Davis ptr.x32 + i / 4, wlen / 4);
729f4358134SBrooks Davis break;
730f4358134SBrooks Davis }
731f4358134SBrooks Davis
732f4358134SBrooks Davis cfi_write(sc, sc->sc_wrofs + i,
733f4358134SBrooks Davis CFI_BCS_CONFIRM);
734f4358134SBrooks Davis
735f4358134SBrooks Davis intr_restore(intr);
736f4358134SBrooks Davis
737f4358134SBrooks Davis error = cfi_wait_ready(sc, sc->sc_wrofs + i,
738f4358134SBrooks Davis start, CFI_TIMEOUT_BUFWRITE);
739f4358134SBrooks Davis if (error != 0)
740f4358134SBrooks Davis goto out;
741f4358134SBrooks Davis }
742f4358134SBrooks Davis goto out;
743f4358134SBrooks Davis default:
744f4358134SBrooks Davis /* Fall through to single word case */
745f4358134SBrooks Davis break;
746f4358134SBrooks Davis }
747f4358134SBrooks Davis }
748f4358134SBrooks Davis
749f4358134SBrooks Davis /* Write the block one byte/word at a time. */
750e00251b7SMarcel Moolenaar for (i = 0; i < sc->sc_wrbufsz; i += sc->sc_width) {
751f4358134SBrooks Davis /* Avoid writing unless we are actually changing bits */
752f4358134SBrooks Davis if (!neederase) {
753f4358134SBrooks Davis switch (sc->sc_width) {
754f4358134SBrooks Davis case 1:
755f4358134SBrooks Davis if(*(ptr.x8 + i) == *(cpyprt.x8 + i))
756f4358134SBrooks Davis continue;
757f4358134SBrooks Davis break;
758f4358134SBrooks Davis case 2:
759f4358134SBrooks Davis if(*(ptr.x16 + i / 2) == *(cpyprt.x16 + i / 2))
760f4358134SBrooks Davis continue;
761f4358134SBrooks Davis break;
762f4358134SBrooks Davis case 4:
763f4358134SBrooks Davis if(*(ptr.x32 + i / 4) == *(cpyprt.x32 + i / 4))
764f4358134SBrooks Davis continue;
765f4358134SBrooks Davis break;
766f4358134SBrooks Davis }
767f4358134SBrooks Davis }
768f4358134SBrooks Davis
769e00251b7SMarcel Moolenaar /*
770e00251b7SMarcel Moolenaar * Make sure the command to start a write and the
771e00251b7SMarcel Moolenaar * actual write happens back-to-back without any
772e00251b7SMarcel Moolenaar * excessive delays.
773e00251b7SMarcel Moolenaar */
774e00251b7SMarcel Moolenaar intr = intr_disable();
775e00251b7SMarcel Moolenaar
776f4358134SBrooks Davis start = sbinuptime();
777e00251b7SMarcel Moolenaar switch (sc->sc_cmdset) {
778e00251b7SMarcel Moolenaar case CFI_VEND_INTEL_ECS:
779e00251b7SMarcel Moolenaar case CFI_VEND_INTEL_SCS:
780e00251b7SMarcel Moolenaar cfi_write(sc, sc->sc_wrofs + i, CFI_BCS_PROGRAM);
781e00251b7SMarcel Moolenaar break;
782e00251b7SMarcel Moolenaar case CFI_VEND_AMD_SCS:
783e00251b7SMarcel Moolenaar case CFI_VEND_AMD_ECS:
784e00251b7SMarcel Moolenaar cfi_amd_write(sc, 0, AMD_ADDR_START, CFI_AMD_PROGRAM);
785e00251b7SMarcel Moolenaar break;
786e00251b7SMarcel Moolenaar }
787e00251b7SMarcel Moolenaar switch (sc->sc_width) {
788e00251b7SMarcel Moolenaar case 1:
789e00251b7SMarcel Moolenaar bus_space_write_1(sc->sc_tag, sc->sc_handle,
790f4358134SBrooks Davis sc->sc_wrofs + i, *(ptr.x8 + i));
791e00251b7SMarcel Moolenaar break;
792e00251b7SMarcel Moolenaar case 2:
793e00251b7SMarcel Moolenaar bus_space_write_2(sc->sc_tag, sc->sc_handle,
794f4358134SBrooks Davis sc->sc_wrofs + i, *(ptr.x16 + i / 2));
795e00251b7SMarcel Moolenaar break;
796e00251b7SMarcel Moolenaar case 4:
797e00251b7SMarcel Moolenaar bus_space_write_4(sc->sc_tag, sc->sc_handle,
798f4358134SBrooks Davis sc->sc_wrofs + i, *(ptr.x32 + i / 4));
799e00251b7SMarcel Moolenaar break;
800e00251b7SMarcel Moolenaar }
801e00251b7SMarcel Moolenaar
802e00251b7SMarcel Moolenaar intr_restore(intr);
803e00251b7SMarcel Moolenaar
80491dfbef4SAllan Jude if (sc->sc_cmdset == CFI_VEND_AMD_ECS ||
80591dfbef4SAllan Jude sc->sc_cmdset == CFI_VEND_AMD_SCS) {
80691dfbef4SAllan Jude for (j = 0; j < CFI_AMD_MAXCHK; ++j) {
80791dfbef4SAllan Jude switch (sc->sc_width) {
80891dfbef4SAllan Jude case 1:
80991dfbef4SAllan Jude val = *(ptr.x8 + i);
81091dfbef4SAllan Jude break;
81191dfbef4SAllan Jude case 2:
81291dfbef4SAllan Jude val = *(ptr.x16 + i / 2);
81391dfbef4SAllan Jude break;
81491dfbef4SAllan Jude case 4:
81591dfbef4SAllan Jude val = *(ptr.x32 + i / 4);
81691dfbef4SAllan Jude break;
81791dfbef4SAllan Jude }
81891dfbef4SAllan Jude
81991dfbef4SAllan Jude if (cfi_read(sc, sc->sc_wrofs + i) == val)
82091dfbef4SAllan Jude break;
82191dfbef4SAllan Jude
82291dfbef4SAllan Jude DELAY(10);
82391dfbef4SAllan Jude }
82491dfbef4SAllan Jude if (j == CFI_AMD_MAXCHK) {
82591dfbef4SAllan Jude printf("\nCFI Program Verify time out error\n");
82691dfbef4SAllan Jude error = ENXIO;
82791dfbef4SAllan Jude goto out;
82891dfbef4SAllan Jude }
82991dfbef4SAllan Jude } else {
830f4358134SBrooks Davis error = cfi_wait_ready(sc, sc->sc_wrofs, start,
831f4358134SBrooks Davis CFI_TIMEOUT_WRITE);
832e00251b7SMarcel Moolenaar if (error)
833e00251b7SMarcel Moolenaar goto out;
834e00251b7SMarcel Moolenaar }
83591dfbef4SAllan Jude }
836e00251b7SMarcel Moolenaar
837e00251b7SMarcel Moolenaar /* error is 0. */
838e00251b7SMarcel Moolenaar
839e00251b7SMarcel Moolenaar out:
8406bf13692SAdrian Chadd cfi_reset_default(sc);
8415faf6ff4SBrooks Davis
8425faf6ff4SBrooks Davis /* Relock Intel flash */
8435faf6ff4SBrooks Davis switch (sc->sc_cmdset) {
8445faf6ff4SBrooks Davis case CFI_VEND_INTEL_ECS:
8455faf6ff4SBrooks Davis case CFI_VEND_INTEL_SCS:
8465faf6ff4SBrooks Davis cfi_write(sc, sc->sc_wrofs, CFI_INTEL_LBS);
8475faf6ff4SBrooks Davis cfi_write(sc, sc->sc_wrofs, CFI_INTEL_LB);
8485faf6ff4SBrooks Davis cfi_write(sc, sc->sc_wrofs, CFI_BCS_READ_ARRAY);
8495faf6ff4SBrooks Davis break;
8505faf6ff4SBrooks Davis }
851e00251b7SMarcel Moolenaar return (error);
852e00251b7SMarcel Moolenaar }
85363425a7fSSam Leffler
85463425a7fSSam Leffler #ifdef CFI_SUPPORT_STRATAFLASH
85563425a7fSSam Leffler /*
85663425a7fSSam Leffler * Intel StrataFlash Protection Register Support.
85763425a7fSSam Leffler *
85863425a7fSSam Leffler * The memory includes a 128-bit Protection Register that can be
85963425a7fSSam Leffler * used for security. There are two 64-bit segments; one is programmed
86063425a7fSSam Leffler * at the factory with a unique 64-bit number which is immutable.
86163425a7fSSam Leffler * The other segment is left blank for User (OEM) programming.
862bd8c9fc0SSam Leffler * The User/OEM segment is One Time Programmable (OTP). It can also
86326b3568cSSam Leffler * be locked to prevent any further writes by setting bit 0 of the
864bd8c9fc0SSam Leffler * Protection Lock Register (PLR). The PLR can written only once.
86563425a7fSSam Leffler */
86663425a7fSSam Leffler
86763425a7fSSam Leffler static uint16_t
cfi_get16(struct cfi_softc * sc,int off)86863425a7fSSam Leffler cfi_get16(struct cfi_softc *sc, int off)
86963425a7fSSam Leffler {
87063425a7fSSam Leffler uint16_t v = bus_space_read_2(sc->sc_tag, sc->sc_handle, off<<1);
87163425a7fSSam Leffler return v;
87263425a7fSSam Leffler }
87363425a7fSSam Leffler
8749313bae4SSam Leffler #ifdef CFI_ARMEDANDDANGEROUS
87563425a7fSSam Leffler static void
cfi_put16(struct cfi_softc * sc,int off,uint16_t v)87663425a7fSSam Leffler cfi_put16(struct cfi_softc *sc, int off, uint16_t v)
87763425a7fSSam Leffler {
87863425a7fSSam Leffler bus_space_write_2(sc->sc_tag, sc->sc_handle, off<<1, v);
87963425a7fSSam Leffler }
8809313bae4SSam Leffler #endif
88163425a7fSSam Leffler
88263425a7fSSam Leffler /*
88363425a7fSSam Leffler * Read the factory-defined 64-bit segment of the PR.
88463425a7fSSam Leffler */
88563425a7fSSam Leffler int
cfi_intel_get_factory_pr(struct cfi_softc * sc,uint64_t * id)88663425a7fSSam Leffler cfi_intel_get_factory_pr(struct cfi_softc *sc, uint64_t *id)
88763425a7fSSam Leffler {
88863425a7fSSam Leffler if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
88963425a7fSSam Leffler return EOPNOTSUPP;
89063425a7fSSam Leffler KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
89163425a7fSSam Leffler
89263425a7fSSam Leffler cfi_write(sc, 0, CFI_INTEL_READ_ID);
89363425a7fSSam Leffler *id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(0)))<<48 |
89463425a7fSSam Leffler ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(1)))<<32 |
89563425a7fSSam Leffler ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(2)))<<16 |
89663425a7fSSam Leffler ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(3)));
89763425a7fSSam Leffler cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
89863425a7fSSam Leffler return 0;
89963425a7fSSam Leffler }
90063425a7fSSam Leffler
90163425a7fSSam Leffler /*
90263425a7fSSam Leffler * Read the User/OEM 64-bit segment of the PR.
90363425a7fSSam Leffler */
90463425a7fSSam Leffler int
cfi_intel_get_oem_pr(struct cfi_softc * sc,uint64_t * id)90563425a7fSSam Leffler cfi_intel_get_oem_pr(struct cfi_softc *sc, uint64_t *id)
90663425a7fSSam Leffler {
90763425a7fSSam Leffler if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
90863425a7fSSam Leffler return EOPNOTSUPP;
90963425a7fSSam Leffler KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
91063425a7fSSam Leffler
91163425a7fSSam Leffler cfi_write(sc, 0, CFI_INTEL_READ_ID);
91263425a7fSSam Leffler *id = ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(4)))<<48 |
91363425a7fSSam Leffler ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(5)))<<32 |
91463425a7fSSam Leffler ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(6)))<<16 |
91563425a7fSSam Leffler ((uint64_t)cfi_get16(sc, CFI_INTEL_PR(7)));
91663425a7fSSam Leffler cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
91763425a7fSSam Leffler return 0;
91863425a7fSSam Leffler }
91963425a7fSSam Leffler
92063425a7fSSam Leffler /*
92163425a7fSSam Leffler * Write the User/OEM 64-bit segment of the PR.
922bd8c9fc0SSam Leffler * XXX should allow writing individual words/bytes
92363425a7fSSam Leffler */
92463425a7fSSam Leffler int
cfi_intel_set_oem_pr(struct cfi_softc * sc,uint64_t id)92563425a7fSSam Leffler cfi_intel_set_oem_pr(struct cfi_softc *sc, uint64_t id)
92663425a7fSSam Leffler {
927bd8c9fc0SSam Leffler #ifdef CFI_ARMEDANDDANGEROUS
92863425a7fSSam Leffler register_t intr;
92963425a7fSSam Leffler int i, error;
930f4358134SBrooks Davis sbintime_t start;
931bd8c9fc0SSam Leffler #endif
93263425a7fSSam Leffler
93363425a7fSSam Leffler if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
93463425a7fSSam Leffler return EOPNOTSUPP;
93563425a7fSSam Leffler KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
93663425a7fSSam Leffler
937bd8c9fc0SSam Leffler #ifdef CFI_ARMEDANDDANGEROUS
93863425a7fSSam Leffler for (i = 7; i >= 4; i--, id >>= 16) {
93963425a7fSSam Leffler intr = intr_disable();
940f4358134SBrooks Davis start = sbinuptime();
94163425a7fSSam Leffler cfi_write(sc, 0, CFI_INTEL_PP_SETUP);
94263425a7fSSam Leffler cfi_put16(sc, CFI_INTEL_PR(i), id&0xffff);
94363425a7fSSam Leffler intr_restore(intr);
944f4358134SBrooks Davis error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, start,
945f4358134SBrooks Davis CFI_TIMEOUT_WRITE);
94663425a7fSSam Leffler if (error)
94763425a7fSSam Leffler break;
94863425a7fSSam Leffler }
94963425a7fSSam Leffler cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
95063425a7fSSam Leffler return error;
951bd8c9fc0SSam Leffler #else
952bd8c9fc0SSam Leffler device_printf(sc->sc_dev, "%s: OEM PR not set, "
953bd8c9fc0SSam Leffler "CFI_ARMEDANDDANGEROUS not configured\n", __func__);
954bd8c9fc0SSam Leffler return ENXIO;
955bd8c9fc0SSam Leffler #endif
95663425a7fSSam Leffler }
95763425a7fSSam Leffler
95863425a7fSSam Leffler /*
95963425a7fSSam Leffler * Read the contents of the Protection Lock Register.
96063425a7fSSam Leffler */
96163425a7fSSam Leffler int
cfi_intel_get_plr(struct cfi_softc * sc,uint32_t * plr)96263425a7fSSam Leffler cfi_intel_get_plr(struct cfi_softc *sc, uint32_t *plr)
96363425a7fSSam Leffler {
96463425a7fSSam Leffler if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
96563425a7fSSam Leffler return EOPNOTSUPP;
96663425a7fSSam Leffler KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
96763425a7fSSam Leffler
96863425a7fSSam Leffler cfi_write(sc, 0, CFI_INTEL_READ_ID);
96963425a7fSSam Leffler *plr = cfi_get16(sc, CFI_INTEL_PLR);
97063425a7fSSam Leffler cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
97163425a7fSSam Leffler return 0;
97263425a7fSSam Leffler }
97363425a7fSSam Leffler
97463425a7fSSam Leffler /*
97563425a7fSSam Leffler * Write the Protection Lock Register to lock down the
97663425a7fSSam Leffler * user-settable segment of the Protection Register.
97763425a7fSSam Leffler * NOTE: this operation is not reversible.
97863425a7fSSam Leffler */
97963425a7fSSam Leffler int
cfi_intel_set_plr(struct cfi_softc * sc)98063425a7fSSam Leffler cfi_intel_set_plr(struct cfi_softc *sc)
98163425a7fSSam Leffler {
98263425a7fSSam Leffler #ifdef CFI_ARMEDANDDANGEROUS
98363425a7fSSam Leffler register_t intr;
98463425a7fSSam Leffler int error;
985f4358134SBrooks Davis sbintime_t start;
9869313bae4SSam Leffler #endif
98763425a7fSSam Leffler if (sc->sc_cmdset != CFI_VEND_INTEL_ECS)
98863425a7fSSam Leffler return EOPNOTSUPP;
98963425a7fSSam Leffler KASSERT(sc->sc_width == 2, ("sc_width %d", sc->sc_width));
99063425a7fSSam Leffler
99163425a7fSSam Leffler #ifdef CFI_ARMEDANDDANGEROUS
99263425a7fSSam Leffler /* worthy of console msg */
99363425a7fSSam Leffler device_printf(sc->sc_dev, "set PLR\n");
99463425a7fSSam Leffler intr = intr_disable();
995f4358134SBrooks Davis binuptime(&start);
99663425a7fSSam Leffler cfi_write(sc, 0, CFI_INTEL_PP_SETUP);
99763425a7fSSam Leffler cfi_put16(sc, CFI_INTEL_PLR, 0xFFFD);
99863425a7fSSam Leffler intr_restore(intr);
999f4358134SBrooks Davis error = cfi_wait_ready(sc, CFI_BCS_READ_STATUS, start,
1000f4358134SBrooks Davis CFI_TIMEOUT_WRITE);
100163425a7fSSam Leffler cfi_write(sc, 0, CFI_BCS_READ_ARRAY);
10029313bae4SSam Leffler return error;
100363425a7fSSam Leffler #else
100463425a7fSSam Leffler device_printf(sc->sc_dev, "%s: PLR not set, "
100563425a7fSSam Leffler "CFI_ARMEDANDDANGEROUS not configured\n", __func__);
10069313bae4SSam Leffler return ENXIO;
100763425a7fSSam Leffler #endif
100863425a7fSSam Leffler }
100963425a7fSSam Leffler #endif /* CFI_SUPPORT_STRATAFLASH */
1010