From b5fa1e4d7779ca3a7f3de7d5cd3f0732ec8a11ff Mon Sep 17 00:00:00 2001
From: Andreas Loehre <alohre@gmail.com>
Date: Thu, 6 Aug 2015 19:41:10 +0200
Subject: [PATCH] flash: at91samd: Add SAML21 variant B device support and fix
 SAMC20/SAMC21

This adds support for the Atmel SAML21 variant B parts.
There is minimal change between the two variants, but in
variant B the automatic page write which the at91samd flash
driver relies on to be enabled is disabled by default.
With this patch the write row function will now issue a page write
command after each of the four pages in the row if the MANW (manual
write) bit is set. This also fixes flash write for the SAMC20/SAMC21
devices which have the MANW bit set by default as well.

I have also moved the device ID (DID) register bitfield extraction
from the find_part into helper macros. These can be used in the future
if there are more workarounds for specific devices.

Tested (programming) on:
ATSAML21-XPRO
ATSAML21-XPRO-B
SAMC21 Xplained Pro
SAMD21 Xplained Pro
SAMD20 Xplained Pro

Change-Id: I401a8aa1efd64730840c0d62cf49a1e880ea5900
Signed-off-by: Andreas Loehre <alohre@gmail.com>
Reviewed-on: http://openocd.zylin.com/2903
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
---
 src/flash/nor/at91samd.c | 56 +++++++++++++++++++++++++++++++++++++---
 1 file changed, 52 insertions(+), 4 deletions(-)

diff --git a/src/flash/nor/at91samd.c b/src/flash/nor/at91samd.c
index 9b0f7eebe..a0bfcc363 100644
--- a/src/flash/nor/at91samd.c
+++ b/src/flash/nor/at91samd.c
@@ -63,6 +63,9 @@
 #define SAMD_NVM_CMD_SSB	0x45		/* Set Security Bit */
 #define SAMD_NVM_CMD_INVALL	0x46		/* Invalidate all caches */
 
+/* NVMCTRL bits */
+#define SAMD_NVM_CTRLB_MANW 0x80
+
 /* Known identifiers */
 #define SAMD_PROCESSOR_M0	0x01
 #define SAMD_FAMILY_D		0x00
@@ -73,6 +76,12 @@
 #define SAMD_SERIES_10		0x02
 #define SAMD_SERIES_11		0x03
 
+/* Device ID macros */
+#define SAMD_GET_PROCESSOR(id) (id >> 28)
+#define SAMD_GET_FAMILY(id) (((id >> 23) & 0x1F))
+#define SAMD_GET_SERIES(id) (((id >> 16) & 0x3F))
+#define SAMD_GET_DEVSEL(id) (id & 0xFF)
+
 struct samd_part {
 	uint8_t id;
 	const char *name;
@@ -166,6 +175,16 @@ static const struct samd_part saml21_parts[] = {
 	{ 0x0B, "SAML21E17A", 128, 16 },
 	{ 0x0C, "SAML21E16A", 64, 8 },
 	{ 0x0D, "SAML21E15A", 32, 4 },
+	{ 0x0F, "SAML21J18B", 256, 32 },
+	{ 0x10, "SAML21J17B", 128, 16 },
+	{ 0x11, "SAML21J16B", 64, 8 },
+	{ 0x14, "SAML21G18B", 256, 32 },
+	{ 0x15, "SAML21G17B", 128, 16 },
+	{ 0x16, "SAML21G16B", 64, 8 },
+	{ 0x19, "SAML21E18B", 256, 32 },
+	{ 0x1A, "SAML21E17B", 128, 16 },
+	{ 0x1B, "SAML21E16B", 64, 8 },
+	{ 0x1C, "SAML21E15B", 32, 4 },
 };
 
 /* Known SAMC20 parts. */
@@ -236,6 +255,7 @@ struct samd_info {
 	int num_pages;
 	int sector_size;
 
+	bool manual_wp;
 	bool probed;
 	struct target *target;
 	struct samd_info *next;
@@ -243,12 +263,14 @@ struct samd_info {
 
 static struct samd_info *samd_chips;
 
+
+
 static const struct samd_part *samd_find_part(uint32_t id)
 {
-	uint8_t processor = (id >> 28);
-	uint8_t family = (id >> 23) & 0x1F;
-	uint8_t series = (id >> 16) & 0x3F;
-	uint8_t devsel = id & 0xFF;
+	uint8_t processor = SAMD_GET_PROCESSOR(id);
+	uint8_t family = SAMD_GET_FAMILY(id);
+	uint8_t series = SAMD_GET_SERIES(id);
+	uint8_t devsel = SAMD_GET_DEVSEL(id);
 
 	for (unsigned i = 0; i < ARRAY_SIZE(samd_families); i++) {
 		if (samd_families[i].processor == processor &&
@@ -362,6 +384,9 @@ static int samd_probe(struct flash_bank *bank)
 
 	samd_protect_check(bank);
 
+	/* By default we do not need to send page write commands */
+	chip->manual_wp = false;
+
 	/* Done */
 	chip->probed = true;
 
@@ -714,6 +739,16 @@ static int samd_write_row(struct flash_bank *bank, uint32_t address,
 			return res;
 		}
 
+		/* For some devices automatic page write is not default so we need
+		 * to issue a write page CMD to the NVM */
+		if (chip->manual_wp == true) {
+			res = samd_issue_nvmctrl_command(bank->target, SAMD_NVM_CMD_WP);
+			if (res != ERROR_OK) {
+				LOG_ERROR("%s: %d", __func__, __LINE__);
+				return res;
+			}
+		}
+
 		/* Access through AHB is stalled while flash is being programmed */
 		usleep(200);
 
@@ -767,6 +802,7 @@ static int samd_write(struct flash_bank *bank, const uint8_t *buffer,
 		uint32_t offset, uint32_t count)
 {
 	int res;
+	uint32_t nvm_ctrlb;
 	uint32_t address;
 	uint32_t nb = 0;
 	struct samd_info *chip = (struct samd_info *)bank->driver_priv;
@@ -783,6 +819,18 @@ static int samd_write(struct flash_bank *bank, const uint8_t *buffer,
 			return ERROR_FLASH_BANK_NOT_PROBED;
 	}
 
+	/* Check if we need to do manual page write commands */
+	res = target_read_u32(bank->target, SAMD_NVMCTRL + SAMD_NVMCTRL_CTRLB, &nvm_ctrlb);
+
+	if (res != ERROR_OK)
+		return res;
+
+	if (nvm_ctrlb & SAMD_NVM_CTRLB_MANW)
+		chip->manual_wp = true;
+	else
+		chip->manual_wp = false;
+
+
 	if (offset % row_size) {
 		/* We're starting at an unaligned offset so we'll write a partial row
 		 * comprising that offset and up to the end of that row. */
-- 
GitLab