flash Kinetis: make FCF protection more user friendly
[fw/openocd] / src / flash / nor / kinetis.c
index cc86f971a4ee4c36a70b4bb1c8a9724685fb3cf5..2c2d062acd530507528061909bede1ebeaa17172 100644 (file)
@@ -287,6 +287,7 @@ struct kinetis_chip {
 
                FS_NO_CMD_BLOCKSTAT = 0x40,
                FS_WIDTH_256BIT = 0x80,
+               FS_ECC = 0x100,
        } flash_support;
 
        enum {
@@ -388,6 +389,7 @@ static const struct kinetis_type kinetis_types_old[] = {
 
 static bool allow_fcf_writes;
 static uint8_t fcf_fopt = 0xff;
+static bool fcf_fopt_configured;
 static bool create_banks;
 
 
@@ -1881,9 +1883,13 @@ static int kinetis_write(struct flash_bank *bank, const uint8_t *buffer,
 {
        int result;
        bool set_fcf = false;
+       bool fcf_in_data_valid = false;
        int sect = 0;
        struct kinetis_flash_bank *k_bank = bank->driver_priv;
        struct kinetis_chip *k_chip = k_bank->k_chip;
+       uint8_t fcf_buffer[FCF_SIZE];
+       uint8_t fcf_current[FCF_SIZE];
+       uint8_t fcf_in_data[FCF_SIZE];
 
        result = kinetis_check_run_mode(k_chip);
        if (result != ERROR_OK)
@@ -1904,11 +1910,41 @@ static int kinetis_write(struct flash_bank *bank, const uint8_t *buffer,
        }
 
        if (set_fcf) {
-               uint8_t fcf_buffer[FCF_SIZE];
-               uint8_t fcf_current[FCF_SIZE];
-
                kinetis_fill_fcf(bank, fcf_buffer);
 
+               fcf_in_data_valid = offset <= FCF_ADDRESS
+                                        && offset + count >= FCF_ADDRESS + FCF_SIZE;
+               if (fcf_in_data_valid) {
+                       memcpy(fcf_in_data, buffer + FCF_ADDRESS - offset, FCF_SIZE);
+                       if (memcmp(fcf_in_data + FCF_FPROT, fcf_buffer, 4)) {
+                               fcf_in_data_valid = false;
+                               LOG_INFO("Flash protection requested in programmed file differs from current setting.");
+                       }
+                       if (fcf_in_data[FCF_FDPROT] != fcf_buffer[FCF_FDPROT]) {
+                               fcf_in_data_valid = false;
+                               LOG_INFO("Data flash protection requested in programmed file differs from current setting.");
+                       }
+                       if ((fcf_in_data[FCF_FSEC] & 3) != 2) {
+                               fcf_in_data_valid = false;
+                               LOG_INFO("Device security requested in programmed file!");
+                       } else if (k_chip->flash_support & FS_ECC
+                           && fcf_in_data[FCF_FSEC] != fcf_buffer[FCF_FSEC]) {
+                               fcf_in_data_valid = false;
+                               LOG_INFO("Strange unsecure mode 0x%02" PRIx8
+                                        "requested in programmed file!",
+                                        fcf_in_data[FCF_FSEC]);
+                       }
+                       if ((k_chip->flash_support & FS_ECC || fcf_fopt_configured)
+                           && fcf_in_data[FCF_FOPT] != fcf_fopt) {
+                               fcf_in_data_valid = false;
+                               LOG_INFO("FOPT requested in programmed file differs from current setting.");
+                       }
+                       if (!fcf_in_data_valid)
+                               LOG_INFO("Expect verify errors at FCF (0x408-0x40f).");
+               }
+       }
+
+       if (set_fcf && !fcf_in_data_valid) {
                if (offset < FCF_ADDRESS) {
                        /* write part preceding FCF */
                        result = kinetis_write_inner(bank, buffer, offset, FCF_ADDRESS - offset);
@@ -1937,9 +1973,10 @@ static int kinetis_write(struct flash_bank *bank, const uint8_t *buffer,
                }
                return result;
 
-       } else
+       } else {
                /* no FCF fiddling, normal write */
                return kinetis_write_inner(bank, buffer, offset, count);
+       }
 }
 
 
@@ -1959,7 +1996,7 @@ static int kinetis_probe_chip(struct kinetis_chip *k_chip)
        unsigned cpu_mhz = 120;
        unsigned idx;
        bool use_nvm_marking = false;
-       char flash_marking[8], nvm_marking[2];
+       char flash_marking[11], nvm_marking[2];
        char name[40];
 
        k_chip->probed = false;
@@ -2126,6 +2163,7 @@ static int kinetis_probe_chip(struct kinetis_chip *k_chip)
                        case KINETIS_SDID_FAMILYID_K6X | KINETIS_SDID_SUBFAMID_KX1:     /* errata 7534 - should be K63 */
                        case KINETIS_SDID_FAMILYID_K6X | KINETIS_SDID_SUBFAMID_KX2:     /* errata 7534 - should be K64 */
                                subfamid += 2; /* errata 7534 fix */
+                               /* fallthrough */
                        case KINETIS_SDID_FAMILYID_K6X | KINETIS_SDID_SUBFAMID_KX3:
                                /* K63FN1M0 */
                        case KINETIS_SDID_FAMILYID_K6X | KINETIS_SDID_SUBFAMID_KX4:
@@ -2145,7 +2183,7 @@ static int kinetis_probe_chip(struct kinetis_chip *k_chip)
                                k_chip->nvm_sector_size = 4<<10;
                                k_chip->max_flash_prog_size = 1<<10;
                                num_blocks = 4;
-                               k_chip->flash_support = FS_PROGRAM_PHRASE | FS_PROGRAM_SECTOR;
+                               k_chip->flash_support = FS_PROGRAM_PHRASE | FS_PROGRAM_SECTOR | FS_ECC;
                                cpu_mhz = 180;
                                break;
 
@@ -2210,6 +2248,45 @@ static int kinetis_probe_chip(struct kinetis_chip *k_chip)
                                 familyid, subfamid, cpu_mhz / 10);
                        break;
 
+               case KINETIS_SDID_SERIESID_KW:
+                       /* Newer KW-series (all KW series except KW2xD, KW01Z) */
+                       cpu_mhz = 48;
+                       switch (k_chip->sim_sdid & (KINETIS_SDID_FAMILYID_MASK | KINETIS_SDID_SUBFAMID_MASK)) {
+                       case KINETIS_SDID_FAMILYID_K4X | KINETIS_SDID_SUBFAMID_KX0:
+                               /* KW40Z */
+                       case KINETIS_SDID_FAMILYID_K3X | KINETIS_SDID_SUBFAMID_KX0:
+                               /* KW30Z */
+                       case KINETIS_SDID_FAMILYID_K2X | KINETIS_SDID_SUBFAMID_KX0:
+                               /* KW20Z */
+                               /* FTFA, 1kB sectors */
+                               k_chip->pflash_sector_size = 1<<10;
+                               k_chip->nvm_sector_size = 1<<10;
+                               /* autodetect 1 or 2 blocks */
+                               k_chip->flash_support = FS_PROGRAM_LONGWORD;
+                               k_chip->cache_type = KINETIS_CACHE_L;
+                               k_chip->watchdog_type = KINETIS_WDOG_COP;
+                               break;
+                       case KINETIS_SDID_FAMILYID_K4X | KINETIS_SDID_SUBFAMID_KX1:
+                               /* KW41Z */
+                       case KINETIS_SDID_FAMILYID_K3X | KINETIS_SDID_SUBFAMID_KX1:
+                               /* KW31Z */
+                       case KINETIS_SDID_FAMILYID_K2X | KINETIS_SDID_SUBFAMID_KX1:
+                               /* KW21Z */
+                               /* FTFA, 2kB sectors */
+                               k_chip->pflash_sector_size = 2<<10;
+                               k_chip->nvm_sector_size = 2<<10;
+                               /* autodetect 1 or 2 blocks */
+                               k_chip->flash_support = FS_PROGRAM_LONGWORD;
+                               k_chip->cache_type = KINETIS_CACHE_L;
+                               k_chip->watchdog_type = KINETIS_WDOG_COP;
+                               break;
+                       default:
+                               LOG_ERROR("Unsupported KW FAMILYID SUBFAMID");
+                       }
+                       snprintf(name, sizeof(name), "MKW%u%uZ%%s%u",
+                                        familyid, subfamid, cpu_mhz / 10);
+                       break;
+
                case KINETIS_SDID_SERIESID_KV:
                        /* KV-series */
                        k_chip->watchdog_type = KINETIS_WDOG_K;
@@ -2260,7 +2337,7 @@ static int kinetis_probe_chip(struct kinetis_chip *k_chip)
                                k_chip->max_flash_prog_size = 1<<10;
                                num_blocks = 1;
                                maxaddr_shift = 14;
-                               k_chip->flash_support = FS_PROGRAM_PHRASE | FS_PROGRAM_SECTOR | FS_WIDTH_256BIT;
+                               k_chip->flash_support = FS_PROGRAM_PHRASE | FS_PROGRAM_SECTOR | FS_WIDTH_256BIT | FS_ECC;
                                k_chip->pflash_base = 0x10000000;
                                k_chip->progr_accel_ram = 0x18000000;
                                cpu_mhz = 240;
@@ -2348,7 +2425,9 @@ static int kinetis_probe_chip(struct kinetis_chip *k_chip)
 
        if (num_blocks == 0)
                num_blocks = k_chip->fcfg2_maxaddr1_shifted ? 2 : 1;
-       else if (k_chip->fcfg2_maxaddr1_shifted == 0 && num_blocks >= 2) {
+       else if (k_chip->fcfg2_maxaddr1_shifted == 0 && num_blocks >= 2 && fcfg2_pflsh) {
+               /* fcfg2_maxaddr1 may be zero due to partitioning whole NVM as EEPROM backup
+                * Do not adjust block count in this case! */
                num_blocks = 1;
                LOG_WARNING("MAXADDR1 is zero, number of flash banks adjusted to 1");
        } else if (k_chip->fcfg2_maxaddr1_shifted != 0 && num_blocks == 1) {
@@ -2405,6 +2484,7 @@ static int kinetis_probe_chip(struct kinetis_chip *k_chip)
                case 0x06:
                        k_chip->dflash_size = k_chip->nvm_size - (4096 << fcfg1_depart);
                        break;
+               case 0x07:
                case 0x08:
                        k_chip->dflash_size = 0;
                        break;
@@ -2422,6 +2502,10 @@ static int kinetis_probe_chip(struct kinetis_chip *k_chip)
        }
 
        switch (fcfg1_pfsize) {
+       case 0x00:
+               k_chip->pflash_size = 8192;
+               break;
+       case 0x01:
        case 0x03:
        case 0x05:
        case 0x07:
@@ -2432,6 +2516,7 @@ static int kinetis_probe_chip(struct kinetis_chip *k_chip)
                break;
        case 0x0f:
                /* a peculiar case: Freescale states different sizes for 0xf
+                * KL03P24M48SF0RM      32 KB .... duplicate of code 0x3
                 * K02P64M100SFARM      128 KB ... duplicate of code 0x7
                 * K22P121M120SF8RM     256 KB ... duplicate of code 0x9
                 * K22P121M120SF7RM     512 KB ... duplicate of code 0xb
@@ -2458,8 +2543,13 @@ static int kinetis_probe_chip(struct kinetis_chip *k_chip)
                /* Program section size is equal to sector size by default */
        }
 
-       k_chip->num_pflash_blocks = num_blocks / (2 - fcfg2_pflsh);
-       k_chip->num_nvm_blocks = num_blocks - k_chip->num_pflash_blocks;
+       if (fcfg2_pflsh) {
+               k_chip->num_pflash_blocks = num_blocks;
+               k_chip->num_nvm_blocks = 0;
+       } else {
+               k_chip->num_pflash_blocks = (num_blocks + 1) / 2;
+               k_chip->num_nvm_blocks = num_blocks - k_chip->num_pflash_blocks;
+       }
 
        if (use_nvm_marking) {
                nvm_marking[0] = k_chip->num_nvm_blocks ? 'X' : 'N';
@@ -2523,7 +2613,7 @@ static int kinetis_probe(struct flash_bank *bank)
                 * parts with more than 32K of PFlash. For parts with
                 * less the protection unit is set to 1024 bytes */
                k_bank->protection_size = MAX(k_chip->pflash_size / 32, 1024);
-               bank->num_prot_blocks = 32 / k_chip->num_pflash_blocks;
+               bank->num_prot_blocks = bank->size / k_bank->protection_size;
                k_bank->protection_block = bank->num_prot_blocks * k_bank->bank_number;
 
                size_k = bank->size / 1024;
@@ -2906,10 +2996,12 @@ COMMAND_HANDLER(kinetis_fopt_handler)
        if (CMD_ARGC > 1)
                return ERROR_COMMAND_SYNTAX_ERROR;
 
-       if (CMD_ARGC == 1)
+       if (CMD_ARGC == 1) {
                fcf_fopt = (uint8_t)strtoul(CMD_ARGV[0], NULL, 0);
-       else
+               fcf_fopt_configured = true;
+       } else {
                command_print(CMD_CTX, "FCF_FOPT 0x%02" PRIx8, fcf_fopt);
+       }
 
        return ERROR_OK;
 }