Draw struct flashchip into struct flashprog_flashctx

We used to have a reference to a dynamically allocated  struct flashchip
inside the flash context. But now that we only ever allocate both in one
go, we can let go of the reference and make it a single structure.

Change-Id: I857acb1def6d133a5cbc28fc7a99b7b1a22b55e0
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.sourcearcade.org/c/flashprog/+/494
diff --git a/amd_rom3read.c b/amd_rom3read.c
index 98ba5d2..2d93128 100644
--- a/amd_rom3read.c
+++ b/amd_rom3read.c
@@ -69,12 +69,12 @@
 		flash_size = estimate_addressable_size(rom3, 64*MiB);
 	}
 
-	flash->chip->total_size = flash_size / KiB;
-	flash->chip->feature_bits |= FEATURE_NO_ERASE;
-	flash->chip->tested = (struct flashprog_test_status)
+	flash->chip.total_size = flash_size / KiB;
+	flash->chip.feature_bits |= FEATURE_NO_ERASE;
+	flash->chip.tested = (struct flashprog_test_status)
 		{ .probe = OK, .read = OK, .erase = NA, .write = NA, .block_protection = NA };
 
-	return !!flash->chip->total_size;
+	return !!flash->chip.total_size;
 }
 
 static int rom3read_read(struct flashctx *const flash, uint8_t *buf, unsigned int start, unsigned int len)
diff --git a/at45db.c b/at45db.c
index f8d9983..25de50d 100644
--- a/at45db.c
+++ b/at45db.c
@@ -81,9 +81,9 @@
 	unsigned int i, j;
 	unsigned int cnt = 0;
 	for (i = 0; i < NUM_ERASEFUNCTIONS; i++) {
-		if (flash->chip->block_erasers[i].block_erase == &spi_erase_at45db_sector) {
+		if (flash->chip.block_erasers[i].block_erase == &spi_erase_at45db_sector) {
 			for (j = 0; j < NUM_ERASEREGIONS; j++) {
-				cnt += flash->chip->block_erasers[i].eraseblocks[j].count;
+				cnt += flash->chip.block_erasers[i].eraseblocks[j].count;
 			}
 		}
 	}
@@ -144,7 +144,7 @@
 	}
 
 	/* AT45DB321C does not support lockdown or a page size of a power of 2... */
-	const bool isAT45DB321C = (strcmp(flash->chip->name, "AT45DB321C") == 0);
+	const bool isAT45DB321C = (strcmp(flash->chip.name, "AT45DB321C") == 0);
 	msg_cdbg("Chip status register is 0x%02x\n", status);
 	msg_cdbg("Chip status register: Bit 7 / Ready is %sset\n", (status & AT45DB_READY) ? "" : "not ");
 	msg_cdbg("Chip status register: Bit 6 / Compare match is %sset\n", (status & AT45DB_CMP) ? "" : "not ");
@@ -174,7 +174,7 @@
 /* Adapt chip entry for AT45DB* chips that support multiple page sizes. */
 int spi_prepare_at45db(struct flashctx *const flash)
 {
-	struct flashchip *const chip = flash->chip;
+	struct flashchip *const chip = &flash->chip;
 	uint8_t status;
 
 	/* Power-of-2 check */
@@ -233,7 +233,7 @@
 
 int spi_read_at45db(struct flashctx *flash, uint8_t *buf, unsigned int addr, unsigned int len)
 {
-	const unsigned int page_size = flash->chip->page_size;
+	const unsigned int page_size = flash->chip.page_size;
 	const unsigned int total_size = flashprog_flash_getsize(flash);
 	if ((addr + len) > total_size) {
 		msg_cerr("%s: tried to read beyond flash boundary: addr=%u, len=%u, size=%u\n",
@@ -265,7 +265,7 @@
  * The first 4 (dummy) bytes read need to be discarded. */
 int spi_read_at45db_e8(struct flashctx *flash, uint8_t *buf, unsigned int addr, unsigned int len)
 {
-	const unsigned int page_size = flash->chip->page_size;
+	const unsigned int page_size = flash->chip.page_size;
 	const unsigned int total_size = flashprog_flash_getsize(flash);
 	if ((addr + len) > total_size) {
 		msg_cerr("%s: tried to read beyond flash boundary: addr=%u, len=%u, size=%u\n",
@@ -343,7 +343,7 @@
 
 int spi_erase_at45db_page(struct flashctx *flash, unsigned int addr, unsigned int blocklen)
 {
-	const unsigned int page_size = flash->chip->page_size;
+	const unsigned int page_size = flash->chip.page_size;
 	const unsigned int total_size = flashprog_flash_getsize(flash);
 
 	if ((addr % page_size) != 0 || (blocklen % page_size) != 0) {
@@ -363,7 +363,7 @@
 
 int spi_erase_at45db_block(struct flashctx *flash, unsigned int addr, unsigned int blocklen)
 {
-	const unsigned int page_size = flash->chip->page_size;
+	const unsigned int page_size = flash->chip.page_size;
 	const unsigned int total_size = flashprog_flash_getsize(flash);
 
 	if ((addr % page_size) != 0 || (blocklen % page_size) != 0) { // FIXME: should check blocks not pages
@@ -383,7 +383,7 @@
 
 int spi_erase_at45db_sector(struct flashctx *flash, unsigned int addr, unsigned int blocklen)
 {
-	const unsigned int page_size = flash->chip->page_size;
+	const unsigned int page_size = flash->chip.page_size;
 	const unsigned int total_size = flashprog_flash_getsize(flash);
 
 	if ((addr % page_size) != 0 || (blocklen % page_size) != 0) { // FIXME: should check sectors not pages
@@ -420,9 +420,9 @@
  * address and has an asymmetric layout. */
 int spi_erase_at45cs_sector(struct flashctx *flash, unsigned int addr, unsigned int blocklen)
 {
-	const unsigned int page_size = flash->chip->page_size;
+	const unsigned int page_size = flash->chip.page_size;
 	const unsigned int total_size = flashprog_flash_getsize(flash);
-	const struct block_eraser be = flash->chip->block_erasers[0];
+	const struct block_eraser be = flash->chip.block_erasers[0];
 	const unsigned int sec_0a_top = be.eraseblocks[0].size;
 	const unsigned int sec_0b_top = be.eraseblocks[0].size + be.eraseblocks[1].size;
 
@@ -459,7 +459,7 @@
 
 static int at45db_fill_buffer1(struct flashctx *flash, const uint8_t *bytes, unsigned int off, unsigned int len)
 {
-	const unsigned int page_size = flash->chip->page_size;
+	const unsigned int page_size = flash->chip.page_size;
 	if ((off + len) > page_size) {
 		msg_cerr("Tried to write %u bytes at offset %u into a buffer of only %u B.\n",
 			 len, off, page_size);
@@ -517,7 +517,7 @@
 
 static int at45db_program_page(struct flashctx *flash, const uint8_t *buf, unsigned int at45db_addr)
 {
-	int ret = at45db_fill_buffer1(flash, buf, 0, flash->chip->page_size);
+	int ret = at45db_fill_buffer1(flash, buf, 0, flash->chip.page_size);
 	if (ret != 0) {
 		msg_cerr("%s: filling the buffer failed!\n", __func__);
 		return ret;
@@ -534,7 +534,7 @@
 
 int spi_write_at45db(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len)
 {
-	const unsigned int page_size = flash->chip->page_size;
+	const unsigned int page_size = flash->chip.page_size;
 	const unsigned int total_size = flashprog_flash_getsize(flash);
 
 	if ((start % page_size) != 0 || (len % page_size) != 0) {
diff --git a/cli_classic.c b/cli_classic.c
index 916845a..d712bde 100644
--- a/cli_classic.c
+++ b/cli_classic.c
@@ -211,7 +211,7 @@
 
 	msg_pdbg("Chip size %u kB is bigger than supported size %zu kB of\n"
 		 "chipset/board/programmer for memory-mapped interface.\n",
-		 flash->chip->total_size, flash->mst.common->max_rom_decode / KiB);
+		 flash->chip.total_size, flash->mst.common->max_rom_decode / KiB);
 
 	if (!force) {
 		msg_cerr("This flash chip is too big for this programmer (--verbose/-V gives details).\n"
@@ -507,7 +507,7 @@
 		ret = 1;
 		goto out_shutdown;
 	}
-	chip = fill_flash->chip;
+	chip = &fill_flash->chip;
 
 	if (show_progress)
 		flashprog_set_progress_callback(fill_flash, &flashprog_progress_cb, NULL);
diff --git a/cli_config.c b/cli_config.c
index 5275fe2..83d86d3 100644
--- a/cli_config.c
+++ b/cli_config.c
@@ -37,7 +37,7 @@
 {
 	switch (setting) {
 	case QUAD_ENABLE:
-		return &flash->chip->reg_bits.qe;
+		return &flash->chip.reg_bits.qe;
 	default:
 		return NULL;
 	}
@@ -245,7 +245,7 @@
 		goto shutdown_ret;
 	}
 
-	if (flash->chip->bustype != BUS_SPI || flash->chip->spi_cmd_set != SPI25) {
+	if (flash->chip.bustype != BUS_SPI || flash->chip.spi_cmd_set != SPI25) {
 		fprintf(stderr, "Only SPI25 flash chips are supported.\n");
 		goto shutdown_ret;
 	}
diff --git a/dediprog.c b/dediprog.c
index d8a452f..f03af7e 100644
--- a/dediprog.c
+++ b/dediprog.c
@@ -435,7 +435,7 @@
 	if (cmd_len < 0)
 		return -1;
 
-	if (flash->chip->feature_bits & FEATURE_4BA_EAR_ANY) {
+	if (flash->chip.feature_bits & FEATURE_4BA_EAR_ANY) {
 		if (spi_set_extended_address(flash, start >> 24))
 			return -1;
 	} else if (start >> 24) {
@@ -480,7 +480,7 @@
 			return -1;
 
 		if (dp_spi_cmd == WRITE_MODE_PAGE_PGM
-		    && (flash->chip->feature_bits & FEATURE_4BA_WRITE)) {
+		    && (flash->chip.feature_bits & FEATURE_4BA_WRITE)) {
 			cmd_buf[3] = WRITE_MODE_4B_ADDR_256B_PAGE_PGM_0x12;
 			cmd_buf[4] = JEDEC_BYTE_PROGRAM_4BA;
 		}
@@ -525,7 +525,7 @@
 			return -1;
 
 		if (dp_spi_cmd == WRITE_MODE_PAGE_PGM) {
-			if (flash->chip->feature_bits & FEATURE_4BA_WRITE) {
+			if (flash->chip.feature_bits & FEATURE_4BA_WRITE) {
 				cmd_buf[3] = WRITE_MODE_4B_ADDR_256B_PAGE_PGM;
 				cmd_buf[4] = JEDEC_BYTE_PROGRAM_4BA;
 			} else if (flash->in_4ba_mode) {
@@ -767,7 +767,7 @@
 			      unsigned int start, unsigned int len, uint8_t dedi_spi_cmd)
 {
 	int ret;
-	const unsigned int chunksize = flash->chip->page_size;
+	const unsigned int chunksize = flash->chip.page_size;
 	unsigned int residue = start % chunksize ? chunksize - start % chunksize : 0;
 	unsigned int bulklen;
 	const struct dediprog_data *dp_data = flash->mst.spi->data;
@@ -820,7 +820,7 @@
 {
 	/* We can write only up to 65535 pages at once: */
 	while (len) {
-		const size_t len_here = MIN(len, flash->chip->page_size * MAX_BLOCK_COUNT);
+		const size_t len_here = MIN(len, flash->chip.page_size * MAX_BLOCK_COUNT);
 		const int ret = dediprog_spi_write(flash, buf, start, len_here, dedi_spi_cmd);
 		if (ret)
 			return ret;
diff --git a/edi.c b/edi.c
index efe41c5..e6a51a1 100644
--- a/edi.c
+++ b/edi.c
@@ -291,7 +291,7 @@
 	unsigned int timeout = 64;
 	int rc;
 
-	if (size != flash->chip->page_size) {
+	if (size != flash->chip.page_size) {
 		msg_perr("%s: Block erase size is not page size!\n", __func__);
 		return -1;
 	}
@@ -338,17 +338,17 @@
 	unsigned int i, j;
 	int rc;
 
-	if ((start % flash->chip->page_size) != 0) {
+	if ((start % flash->chip.page_size) != 0) {
 		msg_perr("%s: Start address is not page-aligned!\n", __func__);
 		return -1;
 	}
 
-	if ((len % flash->chip->page_size) != 0) {
+	if ((len % flash->chip.page_size) != 0) {
 		msg_perr("%s: Length is not page-aligned!\n", __func__);
 		return -1;
 	}
 
-	pages = len / flash->chip->page_size;
+	pages = len / flash->chip.page_size;
 
 	rc = edi_spi_enable(spi);
 	if (rc < 0) {
@@ -364,7 +364,7 @@
 		if (rc < 0)
 			return -1;
 
-		for (j = 0; j < flash->chip->page_size; j++) {
+		for (j = 0; j < flash->chip.page_size; j++) {
 			rc = edi_spi_address(spi, start, address);
 			if (rc < 0)
 				return -1;
@@ -396,7 +396,7 @@
 			return -1;
 		}
 
-		flashprog_progress_add(flash, flash->chip->page_size);
+		flashprog_progress_add(flash, flash->chip.page_size);
 	}
 
 	rc = edi_spi_disable(spi);
diff --git a/flashprog.c b/flashprog.c
index f4f0f89..7bd6e63 100644
--- a/flashprog.c
+++ b/flashprog.c
@@ -374,7 +374,7 @@
 int flashprog_read_range(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len)
 {
 	flashprog_progress_start(flash, FLASHPROG_PROGRESS_READ, len);
-	const int ret = flash->chip->read(flash, buf, start, len);
+	const int ret = flash->chip.read(flash, buf, start, len);
 	flashprog_progress_finish(flash);
 	return ret;
 }
@@ -404,7 +404,7 @@
 		return -1;
 	}
 
-	int ret = flash->chip->read(flash, readbuf, start, len);
+	int ret = flash->chip.read(flash, readbuf, start, len);
 	if (ret) {
 		msg_gerr("Verification impossible because read failed "
 			 "at 0x%x (len 0x%x)\n", start, len);
@@ -672,7 +672,7 @@
 
 static int check_block_eraser(const struct flashctx *flash, int k, int log)
 {
-	struct block_eraser eraser = flash->chip->block_erasers[k];
+	struct block_eraser eraser = flash->chip.block_erasers[k];
 
 	if (!eraser.block_erase && !eraser.eraseblocks[0].count) {
 		if (log)
@@ -692,7 +692,7 @@
 		return 1;
 	}
 
-	if (flash->chip->bustype == BUS_SPI && flash->chip->spi_cmd_set == SPI25) {
+	if (flash->chip.bustype == BUS_SPI && flash->chip.spi_cmd_set == SPI25) {
 		bool native_4ba;
 		int i;
 
@@ -736,7 +736,7 @@
 		const chipoff_t region_start	= entry->start;
 		const chipsize_t region_len	= entry->end - entry->start + 1;
 
-		if (flashctx->chip->read(flashctx, buffer + region_start, region_start, region_len))
+		if (flashctx->chip.read(flashctx, buffer + region_start, region_start, region_len))
 			return 1;
 	}
 
@@ -854,7 +854,7 @@
  */
 static int create_erase_layout(struct flashctx *const flashctx, struct erase_layout **e_layout)
 {
-	const struct flashchip *chip = flashctx->chip;
+	const struct flashchip *chip = &flashctx->chip;
 	const size_t erasefn_count = flashprog_count_usable_erasers(flashctx);
 
 	if (!erasefn_count) {
@@ -949,7 +949,7 @@
 			const uint8_t erased_value  = ERASED_VALUE(flashctx);
 			ll->selected = need_erase(
 				info->curcontents + write_start, info->newcontents + write_start,
-				write_len, flashctx->chip->gran, erased_value);
+				write_len, flashctx->chip.gran, erased_value);
 			if (ll->selected)
 				return eraseblock_size;
 		}
@@ -993,10 +993,10 @@
 	chipsize_t lenhere = 0;
 
 	while ((lenhere = get_next_write(curcontents + starthere, newcontents + starthere,
-					 len - starthere, &starthere, flashctx->chip->gran))) {
+					 len - starthere, &starthere, flashctx->chip.gran))) {
 		if (!writecount++)
 			msg_cdbg("W");
-		if (flashctx->chip->write(flashctx, newcontents + starthere,
+		if (flashctx->chip.write(flashctx, newcontents + starthere,
 					  flash_offset + starthere, lenhere))
 			return 1;
 		starthere += lenhere;
@@ -1060,7 +1060,7 @@
 static int walk_by_layout(struct flashctx *const flashctx, struct walk_info *const info,
 			  const per_blockfn_t per_blockfn)
 {
-	const bool do_erase = explicit_erase(info) || !(flashctx->chip->feature_bits & FEATURE_NO_ERASE);
+	const bool do_erase = explicit_erase(info) || !(flashctx->chip.feature_bits & FEATURE_NO_ERASE);
 	const struct flashprog_layout *const layout = get_layout(flashctx);
 	struct erase_layout *erase_layouts = NULL;
 	const struct romentry *entry = NULL;
@@ -1155,7 +1155,7 @@
 		if (info->region_start > info->erase_start) {
 			const chipoff_t start	= info->erase_start;
 			const chipsize_t len	= info->region_start - info->erase_start;
-			if (flashctx->chip->read(flashctx, backup_contents, start, len)) {
+			if (flashctx->chip.read(flashctx, backup_contents, start, len)) {
 				msg_cerr("Can't read! Aborting.\n");
 				goto _free_ret;
 			}
@@ -1165,7 +1165,7 @@
 			const chipoff_t start     = info->region_end + 1;
 			const chipoff_t rel_start = start - info->erase_start; /* within this erase block */
 			const chipsize_t len      = info->erase_end - info->region_end;
-			if (flashctx->chip->read(flashctx, backup_contents + rel_start, start, len)) {
+			if (flashctx->chip.read(flashctx, backup_contents + rel_start, start, len)) {
 				msg_cerr("Can't read! Aborting.\n");
 				goto _free_ret;
 			}
@@ -1264,7 +1264,7 @@
 		const chipoff_t region_start	= entry->start;
 		const chipsize_t region_len	= entry->end - entry->start + 1;
 
-		if (flashctx->chip->read(flashctx, curcontents + region_start, region_start, region_len))
+		if (flashctx->chip.read(flashctx, curcontents + region_start, region_start, region_len))
 			return 1;
 		if (compare_range(newcontents + region_start, curcontents + region_start,
 				  region_start, region_len))
@@ -1469,7 +1469,7 @@
 static int chip_safety_check(const struct flashctx *flash, int force,
 			     int read_it, int write_it, int erase_it, int verify_it)
 {
-	const struct flashchip *chip = flash->chip;
+	const struct flashchip *chip = &flash->chip;
 
 	if (!programmer_may_write && (write_it || erase_it)) {
 		msg_perr("Write/erase is not working yet on your programmer in "
@@ -1553,8 +1553,8 @@
 
 	/* Given the existence of read locks, we want to unlock for read,
 	   erase and write. */
-	if (flash->chip->unlock)
-		flash->chip->unlock(flash);
+	if (flash->chip.unlock)
+		flash->chip.unlock(flash);
 
 	return 0;
 }
diff --git a/fmap.c b/fmap.c
index e8f5b5b..1f17545 100644
--- a/fmap.c
+++ b/fmap.c
@@ -314,7 +314,7 @@
 {
 	int ret;
 
-	if (!flashctx || !flashctx->chip)
+	if (!flashctx)
 		return 1;
 
 	/*
diff --git a/helpers.c b/helpers.c
index 7e12c3e..48c7f0e 100644
--- a/helpers.c
+++ b/helpers.c
@@ -65,7 +65,7 @@
 int flashprog_limit_chip(struct flashctx *flash)
 {
 	const chipsize_t limit = flash->mst.common->max_rom_decode;
-	struct flashchip *const chip = flash->chip;
+	struct flashchip *const chip = &flash->chip;
 	const chipsize_t chip_size = chip->total_size * 1024;
 	unsigned int usable_erasers = 0;
 	unsigned int i;
diff --git a/ichspi.c b/ichspi.c
index c12bc1c..7109a43 100644
--- a/ichspi.c
+++ b/ichspi.c
@@ -1262,9 +1262,9 @@
 	else
 		msg_cdbg(" with a");
 	msg_cdbg(" density of %d kB.\n", total_size / 1024);
-	flash->chip->total_size = total_size / 1024;
+	flash->chip.total_size = total_size / 1024;
 
-	eraser = &(flash->chip->block_erasers[0]);
+	eraser = &(flash->chip.block_erasers[0]);
 	if (!hwseq_data.only_4k)
 		boundary = (REGREAD32(ICH9_REG_FPB) & FPB_FPBA) << 12;
 	else
@@ -1300,7 +1300,7 @@
 		msg_cdbg("In that range are %d erase blocks with %d B each.\n",
 			 size_high / erase_size_high, erase_size_high);
 	}
-	flash->chip->tested = TEST_OK_PREW;
+	flash->chip.tested = TEST_OK_PREW;
 	return 1;
 }
 
diff --git a/include/flash.h b/include/flash.h
index 5566067..9ab8cde 100644
--- a/include/flash.h
+++ b/include/flash.h
@@ -178,7 +178,7 @@
 #define FEATURE_ANY_QUAD	(FEATURE_QPI_35_F5 | FEATURE_QPI_38_FF | \
 				 FEATURE_FAST_READ_QOUT | FEATURE_FAST_READ_QIO | FEATURE_FAST_READ_QPI4B)
 
-#define ERASED_VALUE(flash)	(((flash)->chip->feature_bits & FEATURE_ERASED_ZERO) ? 0x00 : 0xff)
+#define ERASED_VALUE(flash)	(((flash)->chip.feature_bits & FEATURE_ERASED_ZERO) ? 0x00 : 0xff)
 
 #define OK   FLASHPROG_TEST_OK
 #define NT   FLASHPROG_TEST_NT
@@ -424,7 +424,7 @@
 struct spi_read_op;
 
 struct flashprog_flashctx {
-	struct flashchip *chip;
+	struct flashchip chip;
 	/* FIXME: The memory mappings should be saved in a more structured way. */
 	/* The physical_* fields store the respective addresses in the physical address space of the CPU. */
 	uintptr_t physical_memory;
diff --git a/it87spi.c b/it87spi.c
index 3ac25b1..1fb1acc 100644
--- a/it87spi.c
+++ b/it87spi.c
@@ -355,7 +355,7 @@
 	/* FIXME: The command below seems to be redundant or wrong. */
 	OUTB(0x06, it8716f_flashport + 1);
 	OUTB(((2 + (fast_spi ? 1 : 0)) << 4), it8716f_flashport);
-	for (i = 0; i < flash->chip->page_size; i++)
+	for (i = 0; i < flash->chip.page_size; i++)
 		mmio_writeb(buf[i], (void *)(bios + start + i));
 	OUTB(0, it8716f_flashport);
 	/* Wait until the Write-In-Progress bit is cleared.
@@ -404,7 +404,7 @@
 static int it8716f_spi_chip_write_256(struct flashctx *flash, const uint8_t *buf,
 				      unsigned int start, unsigned int len)
 {
-	const struct flashchip *chip = flash->chip;
+	const struct flashchip *chip = &flash->chip;
 	/*
 	 * IT8716F only allows maximum of 512 kb SPI chip size for memory
 	 * mapped access. It also can't write more than 1+3+256 bytes at once,
diff --git a/jedec.c b/jedec.c
index 1cb540a..e7bfc87 100644
--- a/jedec.c
+++ b/jedec.c
@@ -112,13 +112,13 @@
 
 static unsigned int getaddrmask(const struct flashprog_flashctx *flash)
 {
-	return getaddrmask_from_features(flash->chip->feature_bits);
+	return getaddrmask_from_features(flash->chip.feature_bits);
 }
 
 static void start_program_jedec_common(const struct flashctx *flash, unsigned int mask)
 {
 	chipaddr bios = flash->virtual_memory;
-	bool shifted = (flash->chip->feature_bits & FEATURE_ADDR_SHIFTED);
+	bool shifted = (flash->chip.feature_bits & FEATURE_ADDR_SHIFTED);
 
 	chip_writeb(flash, 0xAA, bios + ((shifted ? 0x2AAA : 0x5555) & mask));
 	chip_writeb(flash, 0x55, bios + ((shifted ? 0x5555 : 0x2AAA) & mask));
@@ -411,10 +411,10 @@
 				     unsigned int pagesize, unsigned int mask)
 {
 	chipaddr bios = flash->virtual_memory;
-	bool shifted = (flash->chip->feature_bits & FEATURE_ADDR_SHIFTED);
+	bool shifted = (flash->chip.feature_bits & FEATURE_ADDR_SHIFTED);
 	unsigned int delay_us = 0;
 
-	if(flash->chip->probe_timing != TIMING_ZERO)
+	if(flash->chip.probe_timing != TIMING_ZERO)
 		delay_us = 10;
 
 	/*  Issue the Sector Erase command   */
@@ -443,10 +443,10 @@
 				    unsigned int blocksize, unsigned int mask)
 {
 	chipaddr bios = flash->virtual_memory;
-	bool shifted = (flash->chip->feature_bits & FEATURE_ADDR_SHIFTED);
+	bool shifted = (flash->chip.feature_bits & FEATURE_ADDR_SHIFTED);
 	unsigned int delay_us = 0;
 
-	if(flash->chip->probe_timing != TIMING_ZERO)
+	if(flash->chip.probe_timing != TIMING_ZERO)
 		delay_us = 10;
 
 	/*  Issue the Sector Erase command   */
@@ -474,10 +474,10 @@
 static int erase_chip_jedec_common(struct flashctx *flash, unsigned int mask)
 {
 	chipaddr bios = flash->virtual_memory;
-	bool shifted = (flash->chip->feature_bits & FEATURE_ADDR_SHIFTED);
+	bool shifted = (flash->chip.feature_bits & FEATURE_ADDR_SHIFTED);
 	unsigned int delay_us = 0;
 
-	if(flash->chip->probe_timing != TIMING_ZERO)
+	if(flash->chip.probe_timing != TIMING_ZERO)
 		delay_us = 10;
 
 	/*  Issue the JEDEC Chip Erase command   */
@@ -613,7 +613,7 @@
 	 * write_jedec have page_size set to max_writechunk_size, so
 	 * we're OK for now.
 	 */
-	unsigned int page_size = flash->chip->page_size;
+	unsigned int page_size = flash->chip.page_size;
 	unsigned int nwrites = (start + len - 1) / page_size;
 
 	/* Warning: This loop has a very unusual condition and body.
@@ -746,7 +746,7 @@
 {
 	// FIXME: this depends on the eraseblocks not to be filled up completely (i.e. to be null-terminated).
 	const struct unlockblock *unlockblocks =
-		(const struct unlockblock *)flash->chip->block_erasers[0].eraseblocks;
+		(const struct unlockblock *)flash->chip.block_erasers[0].eraseblocks;
 	return regspace2_walk_unlockblocks(flash, unlockblocks, &printlock_regspace2_block);
 }
 
@@ -754,7 +754,7 @@
 {
 	// FIXME: this depends on the eraseblocks not to be filled up completely (i.e. to be null-terminated).
 	const struct unlockblock *unlockblocks =
-		(const struct unlockblock *)flash->chip->block_erasers[1].eraseblocks;
+		(const struct unlockblock *)flash->chip.block_erasers[1].eraseblocks;
 	return regspace2_walk_unlockblocks(flash, unlockblocks, &printlock_regspace2_block);
 }
 
@@ -849,7 +849,7 @@
 {
 	// FIXME: this depends on the eraseblocks not to be filled up completely (i.e. to be null-terminated).
 	const struct unlockblock *unlockblocks =
-		(const struct unlockblock *)flash->chip->block_erasers[0].eraseblocks;
+		(const struct unlockblock *)flash->chip.block_erasers[0].eraseblocks;
 	return regspace2_walk_unlockblocks(flash, unlockblocks, &unlock_regspace2_block_generic);
 }
 
@@ -857,6 +857,6 @@
 {
 	// FIXME: this depends on the eraseblocks not to be filled up completely (i.e. to be null-terminated).
 	const struct unlockblock *unlockblocks =
-		(const struct unlockblock *)flash->chip->block_erasers[1].eraseblocks;
+		(const struct unlockblock *)flash->chip.block_erasers[1].eraseblocks;
 	return regspace2_walk_unlockblocks(flash, unlockblocks, &unlock_regspace2_block_generic);
 }
diff --git a/layout.c b/layout.c
index 56c4d1b..382b7e1 100644
--- a/layout.c
+++ b/layout.c
@@ -216,7 +216,7 @@
 {
 	const struct flashprog_layout *const layout = get_layout(flash);
 	const chipsize_t total_size = flashprog_flash_getsize(flash);
-	const size_t gran = gran_to_bytes(flash->chip->gran);
+	const size_t gran = gran_to_bytes(flash->chip.gran);
 	int ret = 0;
 
 	const struct romentry *entry = NULL;
diff --git a/libflashprog.c b/libflashprog.c
index 4f258b5..ef85d06 100644
--- a/libflashprog.c
+++ b/libflashprog.c
@@ -290,14 +290,7 @@
 		return 1;
 	}
 
-	flash->chip = calloc(1, sizeof(*flash->chip));
-	if (!flash->chip) {
-		msg_gerr("Out of memory!\n");
-		free(flash);
-		return 1;
-	}
-
-	*flash->chip = *chip;
+	flash->chip = *chip;
 	flash->mst.common = bus; /* `mst` is a union, so we need only one pointer */
 
 	if (chip->prepare_access && chip->prepare_access(flash)) {
@@ -314,9 +307,9 @@
 		return 1;
 	}
 
-	char *const tmp = flashbuses_to_text(flash->chip->bustype);
+	char *const tmp = flashbuses_to_text(flash->chip.bustype);
 	msg_cinfo("Using %s flash chip \"%s\" (%d kB, %s) ",
-		  flash->chip->vendor, flash->chip->name, flash->chip->total_size, tmp);
+		  flash->chip.vendor, flash->chip.name, flash->chip.total_size, tmp);
 	free(tmp);
 	if (strcmp(flashprog->driver->name, "internal") == 0 && flash->physical_memory != 0)
 		msg_cinfo("mapped at physical address 0x%0*" PRIxPTR ".\n",
@@ -324,8 +317,8 @@
 	else
 		msg_cinfo("on %s.\n", flashprog->driver->name);
 
-	if (flash->chip->printlock)
-		flash->chip->printlock(flash);
+	if (flash->chip.printlock)
+		flash->chip.printlock(flash);
 
 	*flashctx = flash;
 	return 0;
@@ -370,7 +363,7 @@
  */
 size_t flashprog_flash_getsize(const struct flashprog_flashctx *const flashctx)
 {
-	return flashctx->chip->total_size * 1024;
+	return flashctx->chip.total_size * 1024;
 }
 
 /**
@@ -383,10 +376,9 @@
 	if (!flashctx)
 		return;
 
-	if (flashctx->chip->finish_access)
-		flashctx->chip->finish_access(flashctx);
+	if (flashctx->chip.finish_access)
+		flashctx->chip.finish_access(flashctx);
 	flashprog_layout_release(flashctx->default_layout);
-	free(flashctx->chip);
 	free(flashctx);
 }
 
@@ -772,10 +764,10 @@
  */
 enum flashprog_wp_result flashprog_wp_write_cfg(struct flashctx *flash, const struct flashprog_wp_cfg *cfg)
 {
-	if (!flash->chip->wp_write_cfg)
+	if (!flash->chip.wp_write_cfg)
 		return FLASHPROG_WP_ERR_CHIP_UNSUPPORTED;
 
-	return flash->chip->wp_write_cfg(flash, cfg);
+	return flash->chip.wp_write_cfg(flash, cfg);
 }
 
 /**
@@ -789,10 +781,10 @@
  */
 enum flashprog_wp_result flashprog_wp_read_cfg(struct flashprog_wp_cfg *cfg, struct flashctx *flash)
 {
-	if (!flash->chip->wp_read_cfg)
+	if (!flash->chip.wp_read_cfg)
 		return FLASHPROG_WP_ERR_CHIP_UNSUPPORTED;
 
-	return flash->chip->wp_read_cfg(cfg, flash);
+	return flash->chip.wp_read_cfg(cfg, flash);
 }
 
 /**
@@ -809,10 +801,10 @@
  */
 enum flashprog_wp_result flashprog_wp_get_available_ranges(struct flashprog_wp_ranges **list, struct flashprog_flashctx *flash)
 {
-	if (!flash->chip->wp_get_ranges)
+	if (!flash->chip.wp_get_ranges)
 		return FLASHPROG_WP_ERR_CHIP_UNSUPPORTED;
 
-	return flash->chip->wp_get_ranges(list, flash);
+	return flash->chip.wp_get_ranges(list, flash);
 }
 
 /**
diff --git a/linux_mtd.c b/linux_mtd.c
index d4c0600..f7da06e 100644
--- a/linux_mtd.c
+++ b/linux_mtd.c
@@ -178,11 +178,11 @@
 	struct linux_mtd_data *data = flash->mst.opaque->data;
 
 	if (data->no_erase)
-		flash->chip->feature_bits |= FEATURE_NO_ERASE;
-	flash->chip->tested = TEST_OK_PREW;
-	flash->chip->total_size = data->total_size / 1024;	/* bytes -> kB */
-	flash->chip->block_erasers[0].eraseblocks[0].size = data->erasesize;
-	flash->chip->block_erasers[0].eraseblocks[0].count =
+		flash->chip.feature_bits |= FEATURE_NO_ERASE;
+	flash->chip.tested = TEST_OK_PREW;
+	flash->chip.total_size = data->total_size / 1024;	/* bytes -> kB */
+	flash->chip.block_erasers[0].eraseblocks[0].size = data->erasesize;
+	flash->chip.block_erasers[0].eraseblocks[0].count =
 		data->total_size / data->erasesize;
 	return 1;
 }
@@ -191,7 +191,7 @@
 			  unsigned int start, unsigned int len)
 {
 	struct linux_mtd_data *data = flash->mst.opaque->data;
-	unsigned int eb_size = flash->chip->block_erasers[0].eraseblocks[0].size;
+	unsigned int eb_size = flash->chip.block_erasers[0].eraseblocks[0].size;
 	unsigned int i;
 
 	if (fseek(data->dev_fp, start, SEEK_SET) != 0) {
@@ -226,7 +226,7 @@
 				unsigned int start, unsigned int len)
 {
 	struct linux_mtd_data *data = flash->mst.opaque->data;
-	unsigned int chunksize = flash->chip->block_erasers[0].eraseblocks[0].size;
+	unsigned int chunksize = flash->chip.block_erasers[0].eraseblocks[0].size;
 	unsigned int i;
 
 	if (!data->device_is_writeable)
diff --git a/memory_bus.c b/memory_bus.c
index 7219171..d90df8f 100644
--- a/memory_bus.c
+++ b/memory_bus.c
@@ -70,7 +70,7 @@
 	flash->virtual_registers = (chipaddr)ERROR_PTR;
 
 	const chipsize_t size = flashprog_flash_getsize(flash);
-	void *const addr = programmer_map_flash_data(par, size, flash->chip->name);
+	void *const addr = programmer_map_flash_data(par, size, flash->chip.name);
 	if (addr == ERROR_PTR)
 		return 1;
 
@@ -97,7 +97,7 @@
 	void *const addr = programmer_map_flash_region(par, "flash chip registers", base, size);
 	if (addr == ERROR_PTR) {
 		msg_pdbg2("Could not map flash chip registers %s at 0x%0*" PRIxPTR ".\n",
-			 flash->chip->name, PRIxPTR_WIDTH, base);
+			 flash->chip.name, PRIxPTR_WIDTH, base);
 		return 0;
 	}
 	flash->physical_registers = base;
diff --git a/nicintel_eeprom.c b/nicintel_eeprom.c
index e5df1f8..c8bfb05 100644
--- a/nicintel_eeprom.c
+++ b/nicintel_eeprom.c
@@ -106,12 +106,12 @@
 static int nicintel_ee_prepare_i210(struct flashctx *flash)
 {
 	/* Emulated eeprom has a fixed size of 4 KB */
-	flash->chip->total_size = 4;
-	flash->chip->page_size = flashprog_flash_getsize(flash);
-	flash->chip->tested = TEST_OK_PREW;
-	flash->chip->gran = write_gran_1byte_implicit_erase;
-	flash->chip->block_erasers->eraseblocks[0].size = flash->chip->page_size;
-	flash->chip->block_erasers->eraseblocks[0].count = 1;
+	flash->chip.total_size = 4;
+	flash->chip.page_size = flashprog_flash_getsize(flash);
+	flash->chip.tested = TEST_OK_PREW;
+	flash->chip.gran = write_gran_1byte_implicit_erase;
+	flash->chip.block_erasers->eraseblocks[0].size = flash->chip.page_size;
+	flash->chip.block_erasers->eraseblocks[0].count = 1;
 
 	return 1;
 }
@@ -119,16 +119,16 @@
 static int nicintel_ee_prepare_82580(struct flashctx *flash)
 {
 	if (nicintel_pci->device_id == UNPROG_DEVICE)
-		flash->chip->total_size = 16; /* Fall back to minimum supported size. */
+		flash->chip.total_size = 16; /* Fall back to minimum supported size. */
 	else {
 		uint32_t tmp = pci_mmio_readl(nicintel_eebar + EEC);
 		tmp = ((tmp >> EE_SIZE) & EE_SIZE_MASK);
 		switch (tmp) {
 		case 7:
-			flash->chip->total_size = 16;
+			flash->chip.total_size = 16;
 			break;
 		case 8:
-			flash->chip->total_size = 32;
+			flash->chip.total_size = 32;
 			break;
 		default:
 			msg_cerr("Unsupported chip size 0x%x\n", tmp);
@@ -136,11 +136,11 @@
 		}
 	}
 
-	flash->chip->page_size = EE_PAGE_MASK + 1;
-	flash->chip->tested = TEST_OK_PREW;
-	flash->chip->gran = write_gran_1byte_implicit_erase;
-	flash->chip->block_erasers->eraseblocks[0].size = (EE_PAGE_MASK + 1);
-	flash->chip->block_erasers->eraseblocks[0].count = (flashprog_flash_getsize(flash)) / (EE_PAGE_MASK + 1);
+	flash->chip.page_size = EE_PAGE_MASK + 1;
+	flash->chip.tested = TEST_OK_PREW;
+	flash->chip.gran = write_gran_1byte_implicit_erase;
+	flash->chip.block_erasers->eraseblocks[0].size = (EE_PAGE_MASK + 1);
+	flash->chip.block_erasers->eraseblocks[0].count = (flashprog_flash_getsize(flash)) / (EE_PAGE_MASK + 1);
 
 	return 1;
 }
diff --git a/sfdp.c b/sfdp.c
index b1d7777..bb01e79 100644
--- a/sfdp.c
+++ b/sfdp.c
@@ -453,7 +453,7 @@
 				msg_cdbg("Length of the mandatory JEDEC SFDP "
 					 "parameter table is wrong (%d B), "
 					 "skipping it.\n", len);
-			} else if (sfdp_fill_flash(flash->chip, tbuf, len) == 0)
+			} else if (sfdp_fill_flash(&flash->chip, tbuf, len) == 0)
 				ret = 0;
 		}
 		free(tbuf);
@@ -466,7 +466,7 @@
 	if (ret)
 		return ret;
 
-	if (selfcheck_chip(flash->chip, -1)) {
+	if (selfcheck_chip(&flash->chip, -1)) {
 		msg_cerr("SFDP parsing resulted in invalid chip structure.\n");
 		return SPI_FLASHPROG_BUG;
 	}
@@ -489,8 +489,8 @@
 
 	/* Chain preparation in case we replaced the .prepare_access
 	   pointer, e.g. spi_prepare_io() is needed for 4BA mode. */
-	if (flash->chip->prepare_access != spi_prepare_sfdp)
-		ret = flash->chip->prepare_access(flash);
+	if (flash->chip.prepare_access != spi_prepare_sfdp)
+		ret = flash->chip.prepare_access(flash);
 
 	return ret;
 }
diff --git a/spi25.c b/spi25.c
index e5a9ddb..75177f8 100644
--- a/spi25.c
+++ b/spi25.c
@@ -317,9 +317,9 @@
 static int spi_write_extended_address_register(struct flashctx *const flash, const uint8_t regdata)
 {
 	uint8_t op;
-	if (flash->chip->feature_bits & FEATURE_4BA_EAR_C5C8) {
+	if (flash->chip.feature_bits & FEATURE_4BA_EAR_C5C8) {
 		op = JEDEC_WRITE_EXT_ADDR_REG;
-	} else if (flash->chip->feature_bits & FEATURE_4BA_EAR_1716) {
+	} else if (flash->chip.feature_bits & FEATURE_4BA_EAR_1716) {
 		op = ALT_WRITE_EXT_ADDR_REG_17;
 	} else {
 		msg_cerr("Flash misses feature flag for extended-address register.\n");
@@ -359,7 +359,7 @@
 
 static size_t spi_address_length(struct flashctx *const flash, const bool native_4ba)
 {
-	if (flash->chip->spi_cmd_set == SPI25_EEPROM) {
+	if (flash->chip.spi_cmd_set == SPI25_EEPROM) {
 		if (flashprog_flash_getsize(flash) > 64*KiB)
 			return 3;
 		if (flashprog_flash_getsize(flash) > 256)
@@ -394,7 +394,7 @@
 		cmd_buf[4] = (addr >>  0) & 0xff;
 		return len;
 	case 3:
-		if (flash->chip->feature_bits & FEATURE_4BA_EAR_ANY) {
+		if (flash->chip.feature_bits & FEATURE_4BA_EAR_ANY) {
 			if (spi_set_extended_address(flash, rel_addr >> 24))
 				return -1;
 		} else if (rel_addr >> 24) {
@@ -655,7 +655,7 @@
 
 static int spi_nbyte_program(struct flashctx *flash, unsigned int addr, const uint8_t *bytes, unsigned int len)
 {
-	const bool native_4ba = flash->chip->feature_bits & FEATURE_4BA_WRITE && spi_master_4ba(flash);
+	const bool native_4ba = flash->chip.feature_bits & FEATURE_4BA_WRITE && spi_master_4ba(flash);
 	const uint8_t op = native_4ba ? JEDEC_BYTE_PROGRAM_4BA : JEDEC_BYTE_PROGRAM;
 	return spi_write_cmd(flash, op, native_4ba, addr, bytes, len, 10);
 }
@@ -668,7 +668,7 @@
 	if (flash->spi_fast_read)
 		return flash->spi_fast_read;
 
-	if (flash->chip->feature_bits & FEATURE_4BA_READ && spi_master_4ba(flash))
+	if (flash->chip.feature_bits & FEATURE_4BA_READ && spi_master_4ba(flash))
 		return &sio_read_4ba;
 
 	return &sio_read;
@@ -717,7 +717,7 @@
 	 * spi_chip_write_256 have page_size set to max_writechunk_size, so
 	 * we're OK for now.
 	 */
-	unsigned int page_size = flash->chip->page_size;
+	unsigned int page_size = flash->chip.page_size;
 
 	/* Warning: This loop has a very unusual condition and body.
 	 * The loop needs to go through each page with at least one affected
diff --git a/spi25_prepare.c b/spi25_prepare.c
index 03fb54b..ba0e2c8 100644
--- a/spi25_prepare.c
+++ b/spi25_prepare.c
@@ -27,11 +27,11 @@
 	const unsigned char cmd = enter ? JEDEC_ENTER_4_BYTE_ADDR_MODE : JEDEC_EXIT_4_BYTE_ADDR_MODE;
 	int ret = 1;
 
-	if (flash->chip->feature_bits & FEATURE_4BA_ENTER)
+	if (flash->chip.feature_bits & FEATURE_4BA_ENTER)
 		ret = spi_send_command(flash, sizeof(cmd), 0, &cmd, NULL);
-	else if (flash->chip->feature_bits & FEATURE_4BA_ENTER_WREN)
+	else if (flash->chip.feature_bits & FEATURE_4BA_ENTER_WREN)
 		ret = spi_simple_write_cmd(flash, cmd, 0);
-	else if (flash->chip->feature_bits & FEATURE_4BA_ENTER_EAR7)
+	else if (flash->chip.feature_bits & FEATURE_4BA_ENTER_EAR7)
 		ret = spi_set_extended_address(flash, enter ? 0x80 : 0x00);
 
 	if (!ret)
@@ -55,9 +55,9 @@
 	flash->in_4ba_mode = false;
 
 	/* Be careful about 4BA chips and broken masters */
-	if (flash->chip->total_size > 16 * 1024 && spi_master_no_4ba_modes(flash)) {
+	if (flash->chip.total_size > 16 * 1024 && spi_master_no_4ba_modes(flash)) {
 		/* If we can't use native instructions, bail out */
-		if ((flash->chip->feature_bits & FEATURE_4BA_NATIVE) != FEATURE_4BA_NATIVE
+		if ((flash->chip.feature_bits & FEATURE_4BA_NATIVE) != FEATURE_4BA_NATIVE
 		    || !spi_master_4ba(flash)) {
 			msg_cerr("Programmer doesn't support this chip. Aborting.\n");
 			return 1;
@@ -65,7 +65,7 @@
 	}
 
 	/* Enable/disable 4-byte addressing mode if flash chip supports it */
-	if (flash->chip->feature_bits & (FEATURE_4BA_ENTER | FEATURE_4BA_ENTER_WREN | FEATURE_4BA_ENTER_EAR7)) {
+	if (flash->chip.feature_bits & (FEATURE_4BA_ENTER | FEATURE_4BA_ENTER_WREN | FEATURE_4BA_ENTER_EAR7)) {
 		int ret;
 		if (spi_master_4ba(flash))
 			ret = spi_enter_4ba(flash);
@@ -82,7 +82,7 @@
 
 static int spi_enter_qpi(struct flashctx *const flash)
 {
-	const unsigned char cmd = flash->chip->feature_bits & FEATURE_QPI_35_F5 ? 0x35 : 0x38;
+	const unsigned char cmd = flash->chip.feature_bits & FEATURE_QPI_35_F5 ? 0x35 : 0x38;
 	const int ret = spi_send_command(flash, sizeof(cmd), 0, &cmd, NULL);
 	if (!ret) {
 		msg_cdbg("Entered QPI mode.\n");
@@ -93,7 +93,7 @@
 
 static int spi_exit_qpi(struct flashctx *const flash)
 {
-	const unsigned char cmd = flash->chip->feature_bits & FEATURE_QPI_35_F5 ? 0xf5 : 0xff;
+	const unsigned char cmd = flash->chip.feature_bits & FEATURE_QPI_35_F5 ? 0xf5 : 0xff;
 	const int ret = spi_send_command(flash, sizeof(cmd), 0, &cmd, NULL);
 	if (!ret) {
 		msg_cdbg("Left QPI mode.\n");
@@ -114,15 +114,15 @@
 
 	/* Check QE bit if present */
 	flash->volatile_qe_enabled = false;
-	if (flash->chip->reg_bits.qe.reg != INVALID_REG) {
-		const struct reg_bit_info qe = flash->chip->reg_bits.qe;
+	if (flash->chip.reg_bits.qe.reg != INVALID_REG) {
+		const struct reg_bit_info qe = flash->chip.reg_bits.qe;
 		const uint8_t mask = 1 << qe.bit_index;
 		uint8_t reg_val;
 
 		if (spi_read_register(flash, qe.reg, &reg_val)) {
 			reg_val = 0;
 		} else if (!(reg_val & mask) &&
-			   (flash->chip->feature_bits & FEATURE_WRSR_EWSR)) {
+			   (flash->chip.feature_bits & FEATURE_WRSR_EWSR)) {
 			msg_pdbg("Trying to set volatile quad-enable (QE).\n");
 			reg_val |= mask;
 			if (spi_write_register(flash, qe.reg, reg_val, WRSR_VOLATILE_BITS) ||
@@ -135,7 +135,7 @@
 
 		if (!(reg_val & mask)) {
 			msg_cinfo("Quad-enable (QE) bit is unknown or unset, disabling quad i/o.\n");
-			flash->chip->feature_bits &= ~FEATURE_ANY_QUAD;
+			flash->chip.feature_bits &= ~FEATURE_ANY_QUAD;
 		} else {
 			msg_cdbg("Quad-enable (QE) bit is set.\n");
 		}
@@ -143,7 +143,7 @@
 
 	flash->in_qpi_mode = false;
 
-	if (!(flash->chip->feature_bits & (FEATURE_QPI_35_F5 | FEATURE_QPI_38_FF)) || !spi_master_qpi(flash))
+	if (!(flash->chip.feature_bits & (FEATURE_QPI_35_F5 | FEATURE_QPI_38_FF)) || !spi_master_qpi(flash))
 		return 0;
 
 	if (spi_enter_qpi(flash))
@@ -154,25 +154,25 @@
 
 static bool qpi_use_fast_read_qio(const struct flashctx *flash)
 {
-	return flash->chip->feature_bits & FEATURE_SET_READ_PARAMS ||
-		flash->chip->reg_bits.dc[0].reg != INVALID_REG ||
-		(flash->chip->dummy_cycles.qpi_fast_read_qio != 0 &&
-		 (flash->chip->dummy_cycles.qpi_fast_read == 0 ||
-		  flash->chip->dummy_cycles.qpi_fast_read_qio <=
-			flash->chip->dummy_cycles.qpi_fast_read));
+	return flash->chip.feature_bits & FEATURE_SET_READ_PARAMS ||
+		flash->chip.reg_bits.dc[0].reg != INVALID_REG ||
+		(flash->chip.dummy_cycles.qpi_fast_read_qio != 0 &&
+		 (flash->chip.dummy_cycles.qpi_fast_read == 0 ||
+		  flash->chip.dummy_cycles.qpi_fast_read_qio <=
+			flash->chip.dummy_cycles.qpi_fast_read));
 }
 
 static int qpi_dummy_cycles(const struct flashctx *flash)
 {
-	if (flash->chip->feature_bits & FEATURE_SET_READ_PARAMS ||
-	    flash->chip->reg_bits.dc[0].reg != INVALID_REG)
+	if (flash->chip.feature_bits & FEATURE_SET_READ_PARAMS ||
+	    flash->chip.reg_bits.dc[0].reg != INVALID_REG)
 		/* TODO: Index 00 is assumed to be the default.
 		         Could switch to potentially faster params. */
-		return flash->chip->dummy_cycles.qpi_read_params.clks00;
+		return flash->chip.dummy_cycles.qpi_read_params.clks00;
 	else if (qpi_use_fast_read_qio(flash))
-		return flash->chip->dummy_cycles.qpi_fast_read_qio;
+		return flash->chip.dummy_cycles.qpi_fast_read_qio;
 	else
-		return flash->chip->dummy_cycles.qpi_fast_read;
+		return flash->chip.dummy_cycles.qpi_fast_read;
 }
 
 static const struct spi_read_op *select_qpi_fast_read(const struct flashctx *flash)
@@ -182,7 +182,7 @@
 	static const struct spi_read_op fast_read_qio_4ba = { QPI_4_4_4, true, JEDEC_FAST_READ_QIO_4BA, 0xff, 0 };
 
 	if (qpi_use_fast_read_qio(flash)) {
-		if (flash->chip->feature_bits & FEATURE_FAST_READ_QPI4B &&
+		if (flash->chip.feature_bits & FEATURE_FAST_READ_QPI4B &&
 		    spi_master_4ba(flash) && flash->mst.spi->probe_opcode(flash, fast_read_qio_4ba.opcode))
 			return &fast_read_qio_4ba;
 		else
@@ -213,9 +213,9 @@
 
 	unsigned int i;
 	for (i = 0; i < ARRAY_SIZE(mio); ++i) {
-		if (mio[i].op.native_4ba && !(flash->chip->feature_bits & FEATURE_4BA_FAST_READ))
+		if (mio[i].op.native_4ba && !(flash->chip.feature_bits & FEATURE_4BA_FAST_READ))
 			continue;
-		if ((flash->chip->feature_bits & mio[i].feature_check) != mio[i].feature_check)
+		if ((flash->chip.feature_bits & mio[i].feature_check) != mio[i].feature_check)
 			continue;
 		if ((flash->mst.spi->features & mio[i].master_check) != mio[i].master_check)
 			continue;
@@ -281,7 +281,7 @@
 	}
 	if (flash->volatile_qe_enabled) {
 		msg_pdbg("Trying to restore volatile quad-enable (QE) state.\n");
-		const struct reg_bit_info qe = flash->chip->reg_bits.qe;
+		const struct reg_bit_info qe = flash->chip.reg_bits.qe;
 		uint8_t reg_val;
 
 		if (!spi_read_register(flash, qe.reg, &reg_val)) {
diff --git a/spi25_statusreg.c b/spi25_statusreg.c
index 6ba4e45..e7aef36 100644
--- a/spi25_statusreg.c
+++ b/spi25_statusreg.c
@@ -59,7 +59,7 @@
 int spi_write_register(const struct flashctx *flash, enum flash_reg reg,
 		       uint8_t value, enum wrsr_target target)
 {
-	int feature_bits = flash->chip->feature_bits;
+	int feature_bits = flash->chip.feature_bits;
 
 	uint8_t write_cmd[4];
 	size_t write_cmd_len = 0;
@@ -195,7 +195,7 @@
 
 int spi_read_register(const struct flashctx *flash, enum flash_reg reg, uint8_t *value)
 {
-	int feature_bits = flash->chip->feature_bits;
+	int feature_bits = flash->chip.feature_bits;
 	uint8_t read_cmd;
 
 	switch (reg) {
@@ -323,8 +323,8 @@
 
 	if ((status & bp_mask) != 0) {
 		msg_cerr("Block protection could not be disabled!\n");
-		if (flash->chip->printlock)
-			flash->chip->printlock(flash);
+		if (flash->chip.printlock)
+			flash->chip.printlock(flash);
 		return 1;
 	}
 	msg_cdbg("disabled.\n");
@@ -864,7 +864,7 @@
 	spi_prettyprint_status_register_hex(status);
 
 	spi_prettyprint_status_register_srwd(status);
-	if (flash->chip->total_size <= 32 / 8 * 1024) /* N25Q16 and N25Q32: reserved */
+	if (flash->chip.total_size <= 32 / 8 * 1024) /* N25Q16 and N25Q32: reserved */
 		spi_prettyprint_status_register_bit(status, 6);
 	else
 		msg_cdbg("Chip status register: Block Protect 3 (BP3) is %sset\n",
diff --git a/spi95.c b/spi95.c
index 70a2074..475a9dc 100644
--- a/spi95.c
+++ b/spi95.c
@@ -72,7 +72,7 @@
 		return 1;
 	}
 	memset(erased_contents, ERASED_VALUE(flash), blocklen * sizeof(uint8_t));
-	result = spi_write_chunked(flash, erased_contents, 0, blocklen, flash->chip->page_size);
+	result = spi_write_chunked(flash, erased_contents, 0, blocklen, flash->chip.page_size);
 	free(erased_contents);
 	return result;
 }
diff --git a/sst_fwhub.c b/sst_fwhub.c
index cd1465d..05c0876 100644
--- a/sst_fwhub.c
+++ b/sst_fwhub.c
@@ -28,7 +28,7 @@
 
 	blockstatus = chip_readb(flash, registers + offset + 2);
 	msg_cdbg("Lock status for 0x%06x (size 0x%06x) is %02x, ",
-		     offset, flash->chip->page_size, blockstatus);
+		     offset, flash->chip.page_size, blockstatus);
 	switch (blockstatus & 0x3) {
 	case 0x0:
 		msg_cdbg("full access\n");
@@ -69,7 +69,7 @@
 {
 	unsigned int i;
 
-	for (i = 0; i < flashprog_flash_getsize(flash); i += flash->chip->page_size)
+	for (i = 0; i < flashprog_flash_getsize(flash); i += flash->chip.page_size)
 		check_sst_fwhub_block_lock(flash, i);
 
 	return 0;
@@ -80,7 +80,7 @@
 	unsigned int i;
 	int ret = 0;
 
-	for (i = 0; i < flashprog_flash_getsize(flash); i += flash->chip->page_size)
+	for (i = 0; i < flashprog_flash_getsize(flash); i += flash->chip.page_size)
 	{
 		if (clear_sst_fwhub_block_lock(flash, i))
 		{
diff --git a/writeprotect.c b/writeprotect.c
index 81385c1..104a417 100644
--- a/writeprotect.c
+++ b/writeprotect.c
@@ -48,7 +48,7 @@
 	 * the register that contains it, extracts the bit's value, and assign
 	 * it to the appropriate field in the wp_bits structure.
 	 */
-	const struct reg_bit_map *bit_map = &flash->chip->reg_bits;
+	const struct reg_bit_map *bit_map = &flash->chip.reg_bits;
 	bool ignored;
 	size_t i;
 	enum flashprog_wp_result ret;
@@ -150,7 +150,7 @@
 	uint8_t reg_values[MAX_REGISTERS];
 	uint8_t bit_masks[MAX_REGISTERS];	/* masks of valid bits */
 	uint8_t write_masks[MAX_REGISTERS];	/* masks of written bits */
-	get_wp_bits_reg_values(reg_values, bit_masks, write_masks, &flash->chip->reg_bits, bits);
+	get_wp_bits_reg_values(reg_values, bit_masks, write_masks, &flash->chip.reg_bits, bits);
 
 	/* Write each register whose value was updated */
 	for (reg = STATUS1; reg < MAX_REGISTERS; reg++) {
@@ -200,7 +200,7 @@
 /** Get the range selected by a WP configuration. */
 static enum flashprog_wp_result get_wp_range(struct wp_range *range, struct flashctx *flash, const struct wp_bits *bits)
 {
-	flash->chip->decode_range(&range->start, &range->len, bits, flashprog_flash_getsize(flash));
+	flash->chip.decode_range(&range->start, &range->len, bits, flashprog_flash_getsize(flash));
 
 	return FLASHPROG_WP_OK;
 }
@@ -269,7 +269,7 @@
  */
 static enum flashprog_wp_result get_ranges_and_wp_bits(struct flashctx *flash, struct wp_bits bits, struct wp_range_and_bits **ranges, size_t *count)
 {
-	const struct reg_bit_map *reg_bits = &flash->chip->reg_bits;
+	const struct reg_bit_map *reg_bits = &flash->chip.reg_bits;
 	size_t i;
 	/*
 	 * Create a list of bits that affect the chip's protection range in
@@ -451,7 +451,7 @@
 
 static bool chip_supported(struct flashctx *flash)
 {
-	return (flash->chip != NULL) && (flash->chip->decode_range != NULL);
+	return flash->chip.decode_range != NULL;
 }
 
 enum flashprog_wp_result spi_wp_read_cfg(struct flashprog_wp_cfg *cfg, struct flashctx *flash)