Add support for M95M02-A125

Automotive 2 Mbit (256KiB) serial SPI bus EEPROM
PREW tested successfully with use of ch341a programmer
on Linux host 5.2.0-1-MANJARO x86_64

Signed-off-by: Konstantin Grudnev <grudnevkv@gmail.com>
Change-Id: Ic29cd9051c7eac4822d620c299834134f987f01b
Reviewed-on: https://review.coreboot.org/c/flashrom/+/34496
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Nico Huber <nico.h@gmx.de>
diff --git a/Makefile b/Makefile
index 1a20933..b0c242d 100644
--- a/Makefile
+++ b/Makefile
@@ -542,7 +542,7 @@
 CHIP_OBJS = jedec.o stm50.o w39.o w29ee011.o \
 	sst28sf040.o 82802ab.o \
 	sst49lfxxxc.o sst_fwhub.o edi.o flashchips.o spi.o spi25.o spi25_statusreg.o \
-	opaque.o sfdp.o en29lv640b.o at45db.o
+	spi95.o opaque.o sfdp.o en29lv640b.o at45db.o
 
 ###############################################################################
 # Library code.
diff --git a/chipdrivers.h b/chipdrivers.h
index e380878..cb1e877 100644
--- a/chipdrivers.h
+++ b/chipdrivers.h
@@ -202,4 +202,8 @@
 int edi_chip_read(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len);
 int edi_probe_kb9012(struct flashctx *flash);
 
+/* spi95.c */
+int probe_spi_st95(struct flashctx *flash);
+int spi_block_erase_emulation(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+
 #endif /* !__CHIPDRIVERS_H__ */
diff --git a/flashchips.c b/flashchips.c
index 7635942..65110fe 100644
--- a/flashchips.c
+++ b/flashchips.c
@@ -14240,6 +14240,33 @@
 	},
 
 	{
+		.vendor		= "ST",
+		.name		= "M95M02",
+		.bustype	= BUS_SPI,
+		.manufacture_id	= ST_ID,
+		.model_id	= ST_M95M02,
+		.total_size	= 256,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN | FEATURE_NO_ERASE | FEATURE_ERASED_ZERO,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_spi_st95,
+		.probe_timing	= TIMING_ZERO,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {256 * 1024, 1} },
+				.block_erase = spi_block_erase_emulation,
+			}
+		},
+
+		.printlock	= spi_prettyprint_status_register_bp1_srwd,
+		.unlock		= spi_disable_blockprotect_bp1_srwd,
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+		.voltage	= {2500, 5500},
+	},
+
+	{
 		.vendor		= "Sanyo",
 		.name		= "LE25FU106B",
 		.bustype	= BUS_SPI,
diff --git a/flashchips.h b/flashchips.h
index c4b863d..88816c4 100644
--- a/flashchips.h
+++ b/flashchips.h
@@ -852,6 +852,9 @@
 #define ST_M58WR032KT		0x8814
 #define ST_M58WR064KB		0x8811
 #define ST_M58WR064KT		0x8810
+
+#define ST_M95M02		0x0012	/* ST M95XXX 2Mbit (256KiB) */
+
 #define ST_MT28GU01G___1	0x88B0
 #define ST_MT28GU01G___2	0x88B1
 #define ST_MT28GU256___1	0x8901
diff --git a/spi.h b/spi.h
index 0073c71..3f45038 100644
--- a/spi.h
+++ b/spi.h
@@ -28,6 +28,13 @@
 /* INSIZE may be 0x04 for some chips*/
 #define JEDEC_RDID_INSIZE	0x03
 
+/* Some ST M95X model */
+#define ST_M95_RDID		0x83
+#define ST_M95_RDID_3BA_OUTSIZE	0x04	/* 8b op, 24bit addr where size >64KiB */
+#define ST_M95_RDID_2BA_OUTSIZE	0x03	/* 8b op, 16bit addr where size <=64KiB */
+#define ST_M95_RDID_OUTSIZE_MAX 0x04	/* ST_M95_RDID_3BA_OUTSIZE */
+#define ST_M95_RDID_INSIZE	0x03
+
 /* Some Atmel AT25F* models have bit 3 as don't care bit in commands */
 #define AT25F_RDID		0x15	/* 0x15 or 0x1d */
 #define AT25F_RDID_OUTSIZE	0x01
diff --git a/spi95.c b/spi95.c
new file mode 100644
index 0000000..ecb2c1d
--- /dev/null
+++ b/spi95.c
@@ -0,0 +1,69 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2019 Konstantin Grudnev
+ * Copyright (C) 2019 Nikolay Nikolaev
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License,
+ * or any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Contains SPI chip driver functions related to ST95XXX series (SPI EEPROM)
+ */
+#include <string.h>
+#include <stdlib.h>
+#include "flashchips.h"
+#include "chipdrivers.h"
+#include "spi.h"
+
+/* For ST95XXX chips which have RDID */
+int probe_spi_st95(struct flashctx *flash)
+{
+	/*
+	 * ST_M95_RDID_OUTSIZE depends on size of the flash and
+	 * not all ST_M95XXX have RDID.
+	 */
+	static const unsigned char cmd[ST_M95_RDID_OUTSIZE_MAX] = { ST_M95_RDID };
+	unsigned char readarr[ST_M95_RDID_INSIZE];
+	uint32_t id1, id2;
+
+	uint32_t rdid_outsize = ST_M95_RDID_2BA_OUTSIZE; // 16 bit address
+	if (flash->chip->total_size * KiB > 64 * KiB)
+		rdid_outsize = ST_M95_RDID_3BA_OUTSIZE; // 24 bit address
+
+	spi_send_command(flash, rdid_outsize, sizeof(readarr), cmd, readarr);
+
+	id1 = readarr[0]; // manufacture id
+	id2 = (readarr[1] << 8) | readarr[2]; // SPI family code + model id
+
+	msg_cdbg("%s: id1 0x%02x, id2 0x%02x\n", __func__, id1, id2);
+
+	if (id1 == flash->chip->manufacture_id && id2 == flash->chip->model_id)
+		return 1;
+
+	return 0;
+}
+
+/* ST95XXX chips don't have erase operation and erase is made as part of write command */
+int spi_block_erase_emulation(struct flashctx *flash, unsigned int addr, unsigned int blocklen)
+{
+	uint8_t *erased_contents = NULL;
+	int result = 0;
+
+	erased_contents = (uint8_t *)malloc(blocklen * sizeof(uint8_t));
+	if (!erased_contents) {
+		msg_cerr("Out of memory!\n");
+		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);
+	free(erased_contents);
+	return result;
+}