/* * cfi_chip_query - detect a CFI chip * * fill in the struct cfi as we discover what's there */ static bool cfi_chip_query(struct cfi * const cfi) { const bus_size_t cfi_query_offset[] = { CFI_QUERY_MODE_ADDR, CFI_QUERY_MODE_ALT_ADDR }; KASSERT(cfi != NULL); KASSERT(cfi->cfi_bst != NULL); for (int j=0; j < __arraycount(cfi_query_offset); j++) { cfi_reset_default(cfi); cfi_cmd(cfi, cfi_query_offset[j], CFI_QUERY_DATA); if (cfi_read_qry(cfi, 0x10) == 'Q' && cfi_read_qry(cfi, 0x11) == 'R' && cfi_read_qry(cfi, 0x12) == 'Y') { switch(cfi->cfi_portwidth) { case 0: cfi_chip_query_1(cfi); break; case 1: cfi_chip_query_2(cfi); break; case 2: cfi_chip_query_4(cfi); break; case 3: cfi_chip_query_8(cfi); break; default: panic("%s: bad portwidth %d\n", __func__, cfi->cfi_portwidth); } switch (cfi->cfi_qry_data.id_pri) { case 0x0002: cfi->cfi_unlock_addr1 = CFI_AMD_UNLOCK_ADDR1; cfi->cfi_unlock_addr2 = CFI_AMD_UNLOCK_ADDR2; break; default: DPRINTF(("%s: unsupported CFI cmdset %#04x\n", __func__, cfi->cfi_qry_data.id_pri)); return false; } cfi->cfi_emulated = false; return true; } } return false; }
static int cfi_devioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) { struct cfi_softc *sc; struct cfiocqry *rq; int error; u_char val; sc = dev->si_drv1; error = 0; switch (cmd) { case CFIOCQRY: if (sc->sc_writing) { error = cfi_block_finish(sc); if (error) break; } rq = (struct cfiocqry *)data; if (rq->offset >= sc->sc_size / sc->sc_width) return (ESPIPE); if (rq->offset + rq->count > sc->sc_size / sc->sc_width) return (ENOSPC); while (!error && rq->count--) { val = cfi_read_qry(sc, rq->offset++); error = copyout(&val, rq->buffer++, 1); } break; #ifdef CFI_SUPPORT_STRATAFLASH case CFIOCGFACTORYPR: error = cfi_intel_get_factory_pr(sc, (uint64_t *)data); break; case CFIOCGOEMPR: error = cfi_intel_get_oem_pr(sc, (uint64_t *)data); break; case CFIOCSOEMPR: error = cfi_intel_set_oem_pr(sc, *(uint64_t *)data); break; case CFIOCGPLR: error = cfi_intel_get_plr(sc, (uint32_t *)data); break; case CFIOCSPLR: error = cfi_intel_set_plr(sc); break; #endif /* CFI_SUPPORT_STRATAFLASH */ default: error = ENOIOCTL; break; } return (error); }
int cfi_attach(device_t dev) { struct cfi_softc *sc; u_int blksz, blocks; u_int r, u; sc = device_get_softc(dev); sc->sc_dev = dev; sc->sc_rid = 0; sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, RF_ACTIVE); if (sc->sc_res == NULL) return (ENXIO); sc->sc_tag = rman_get_bustag(sc->sc_res); sc->sc_handle = rman_get_bushandle(sc->sc_res); /* Get time-out values for erase and write. */ sc->sc_write_timeout = 1 << cfi_read_qry(sc, CFI_QRY_TTO_WRITE); sc->sc_erase_timeout = 1 << cfi_read_qry(sc, CFI_QRY_TTO_ERASE); sc->sc_write_timeout *= 1 << cfi_read_qry(sc, CFI_QRY_MTO_WRITE); sc->sc_erase_timeout *= 1 << cfi_read_qry(sc, CFI_QRY_MTO_ERASE); /* Get erase regions. */ sc->sc_regions = cfi_read_qry(sc, CFI_QRY_NREGIONS); sc->sc_region = malloc(sc->sc_regions * sizeof(struct cfi_region), M_TEMP, M_WAITOK | M_ZERO); for (r = 0; r < sc->sc_regions; r++) { blocks = cfi_read_qry(sc, CFI_QRY_REGION(r)) | (cfi_read_qry(sc, CFI_QRY_REGION(r) + 1) << 8); sc->sc_region[r].r_blocks = blocks + 1; blksz = cfi_read_qry(sc, CFI_QRY_REGION(r) + 2) | (cfi_read_qry(sc, CFI_QRY_REGION(r) + 3) << 8); sc->sc_region[r].r_blksz = (blksz == 0) ? 128 : blksz * 256; } /* Reset the device to a default state. */ cfi_write(sc, 0, CFI_BCS_CLEAR_STATUS); if (bootverbose) { device_printf(dev, "["); for (r = 0; r < sc->sc_regions; r++) { printf("%ux%s%s", sc->sc_region[r].r_blocks, cfi_fmtsize(sc->sc_region[r].r_blksz), (r == sc->sc_regions - 1) ? "]\n" : ","); } } u = device_get_unit(dev); sc->sc_nod = make_dev(&cfi_cdevsw, u, UID_ROOT, GID_WHEEL, 0600, "%s%u", cfi_driver_name, u); sc->sc_nod->si_drv1 = sc; device_add_child(dev, "cfid", -1); bus_generic_attach(dev); return (0); }
int cfi_probe(device_t dev) { char desc[80]; struct cfi_softc *sc; char *vend_str; int error; uint16_t iface, vend; sc = device_get_softc(dev); sc->sc_dev = dev; sc->sc_rid = 0; sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, RF_ACTIVE); if (sc->sc_res == NULL) return (ENXIO); sc->sc_tag = rman_get_bustag(sc->sc_res); sc->sc_handle = rman_get_bushandle(sc->sc_res); if (sc->sc_width == 0) { sc->sc_width = 1; while (sc->sc_width <= 4) { if (cfi_read_qry(sc, CFI_QRY_IDENT) == 'Q') break; sc->sc_width <<= 1; } } else if (cfi_read_qry(sc, CFI_QRY_IDENT) != 'Q') { error = ENXIO; goto out; } if (sc->sc_width > 4) { error = ENXIO; goto out; } /* We got a Q. Check if we also have the R and the Y. */ if (cfi_read_qry(sc, CFI_QRY_IDENT + 1) != 'R' || cfi_read_qry(sc, CFI_QRY_IDENT + 2) != 'Y') { error = ENXIO; goto out; } /* Get the vendor and command set. */ vend = cfi_read_qry(sc, CFI_QRY_VEND) | (cfi_read_qry(sc, CFI_QRY_VEND + 1) << 8); sc->sc_cmdset = vend; switch (vend) { case CFI_VEND_AMD_ECS: case CFI_VEND_AMD_SCS: vend_str = "AMD/Fujitsu"; break; case CFI_VEND_INTEL_ECS: vend_str = "Intel/Sharp"; break; case CFI_VEND_INTEL_SCS: vend_str = "Intel"; break; case CFI_VEND_MITSUBISHI_ECS: case CFI_VEND_MITSUBISHI_SCS: vend_str = "Mitsubishi"; break; default: vend_str = "Unknown vendor"; break; } /* Get the device size. */ sc->sc_size = 1U << cfi_read_qry(sc, CFI_QRY_SIZE); /* Sanity-check the I/F */ iface = cfi_read_qry(sc, CFI_QRY_IFACE) | (cfi_read_qry(sc, CFI_QRY_IFACE + 1) << 8); /* * Adding 1 to iface will give us a bit-wise "switch" * that allows us to test for the interface width by * testing a single bit. */ iface++; error = (iface & sc->sc_width) ? 0 : EINVAL; if (error) goto out; snprintf(desc, sizeof(desc), "%s - %s", vend_str, cfi_fmtsize(sc->sc_size)); device_set_desc_copy(dev, desc); out: bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res); return (error); }