diff options
author | Raj Rajasekaran <b10872@freescale.com> | 2009-09-24 14:24:25 -0500 |
---|---|---|
committer | Raj Rajasekaran <b10872@freescale.com> | 2009-10-12 13:49:07 -0500 |
commit | 503ce6aa61c66e68af47749b79673554ee1413c3 (patch) | |
tree | ef414ebf8e0b2d7eae810efa4b7dae71f7a66bf6 | |
parent | 11c45f4f5858a15bf4ac13b0c8ae89a2f634b5e1 (diff) |
ENGR00116798:MX51: Add support to handle potential hang on SCC HW failute.
-Added kernel error message to handle hang due to SCC HW failure.
-Eliminated unwanted sleeps.
-Kernel error message to handle the case when SCC key fuses are not blown.
Signed-off-by: Raj Rajasekaran <b10872@freescale.com>
-rw-r--r-- | arch/arm/mach-mx51/devices.c | 167 | ||||
-rw-r--r-- | include/linux/mxc_scc2_driver.h | 2 |
2 files changed, 132 insertions, 37 deletions
diff --git a/arch/arm/mach-mx51/devices.c b/arch/arm/mach-mx51/devices.c index 07cc40c2c5d4..495d1043a93f 100644 --- a/arch/arm/mach-mx51/devices.c +++ b/arch/arm/mach-mx51/devices.c @@ -386,12 +386,13 @@ static struct platform_device mxc_scc_device = { .name = "mxc_scc", .id = 0, }; - static void mxc_init_scc(void) { platform_device_register(&mxc_scc_device); } #else +#define SCM_RD_DELAY 1000000 /* in nanoseconds */ +#define SEC_TO_NANOSEC 1000000000 /*Second to nanoseconds */ static inline void mxc_init_scc(void) { uint32_t reg_value; @@ -404,6 +405,11 @@ static inline void mxc_init_scc(void) void *scm_ram_base; void *scc_base; uint8_t iram_partitions = 16; + struct timespec stime; + struct timespec curtime; + long scm_rd_timeout = 0; + long cur_ns = 0; + long start_ns = 0; if (cpu_is_mx51_rev(CHIP_REV_2_0) < 0) iram_partitions = 12; @@ -419,59 +425,146 @@ static inline void mxc_init_scc(void) return; } - for (partition_no = 0; partition_no < iram_partitions; partition_no++) { - /*De-allocate a Partition*/ - reg_value = ((partition_no << SCM_ZCMD_PART_SHIFT) & - SCM_ZCMD_PART_MASK) | ((0x03 << - SCM_ZCMD_CCMD_SHIFT) - & SCM_ZCMD_CCMD_MASK); - __raw_writel(reg_value, scc_base + SCM_ZCMD_REG); - msleep(1); - while ((__raw_readl(scc_base + SCM_STATUS_REG) & - SCM_STATUS_SRS_READY) != SCM_STATUS_SRS_READY) ; + /* Wait for any running SCC operations to finish or fail */ + getnstimeofday(&stime); + do { + reg_value = __raw_readl(scc_base + SCM_STATUS_REG); + getnstimeofday(&curtime); + if (curtime.tv_nsec > stime.tv_nsec) + scm_rd_timeout = curtime.tv_nsec - stime.tv_nsec; + else{ + /*Converted second to nanosecond and add to + nsec when current nanosec is less than + start time nanosec.*/ + cur_ns = (curtime.tv_sec * SEC_TO_NANOSEC) + + curtime.tv_nsec; + start_ns = (stime.tv_sec * SEC_TO_NANOSEC) + + stime.tv_nsec; + scm_rd_timeout = cur_ns - start_ns; + } + } while (((reg_value & SCM_STATUS_SRS_MASK) != SCM_STATUS_SRS_READY) + && ((reg_value & SCM_STATUS_SRS_MASK) != SCM_STATUS_SRS_FAIL)); - /*In Supervisor mode claims a partition for it's own use - by writing zero to SMID register.*/ - __raw_writel(0, scc_base + (SCM_SMID0_REG + 8 * partition_no)); + /* Check for failures */ + if ((reg_value & SCM_STATUS_SRS_MASK) != SCM_STATUS_SRS_READY) { + /* Special message for bad secret key fuses */ + if (reg_value & SCM_STATUS_KST_BAD_KEY) + printk(KERN_ERR "INVALID SCC KEY FUSE PATTERN\n"); + else + printk(KERN_ERR "SECURE RAM FAILURE\n"); - reg_mask |= (3 << (2 * (partition_no))); + iounmap(scm_ram_base); + iounmap(scc_base); + return; } - msleep(1); - reg_value = __raw_readl(scc_base + SCM_PART_OWNERS_REG); + scm_rd_timeout = 0; + /* Release final two partitions for SCC2 driver */ + scc_partno = iram_partitions - (SCC_IRAM_SIZE / SZ_8K); + for (partition_no = scc_partno; partition_no < iram_partitions; + partition_no++) { + reg_value = (((partition_no << SCM_ZCMD_PART_SHIFT) & + SCM_ZCMD_PART_MASK) | ((0x03 << SCM_ZCMD_CCMD_SHIFT) & + SCM_ZCMD_CCMD_MASK)); + __raw_writel(reg_value, scc_base + SCM_ZCMD_REG); + udelay(1); + /* Wait for zeroization to complete */ + getnstimeofday(&stime); + do { + reg_value = __raw_readl(scc_base + SCM_STATUS_REG); + getnstimeofday(&curtime); + if (curtime.tv_nsec > stime.tv_nsec) + scm_rd_timeout = curtime.tv_nsec - + stime.tv_nsec; + else { + /*Converted second to nanosecond and add to + nsec when current nanosec is less than + start time nanosec.*/ + cur_ns = (curtime.tv_sec * SEC_TO_NANOSEC) + + curtime.tv_nsec; + start_ns = (stime.tv_sec * SEC_TO_NANOSEC) + + stime.tv_nsec; + scm_rd_timeout = cur_ns - start_ns; + } + } while (((reg_value & SCM_STATUS_SRS_MASK) != + SCM_STATUS_SRS_READY) && ((reg_value & SCM_STATUS_SRS_MASK) != + SCM_STATUS_SRS_FAIL) && (scm_rd_timeout <= SCM_RD_DELAY)); + + if (scm_rd_timeout > SCM_RD_DELAY) + printk(KERN_ERR "SCM Status Register Read timeout" + "for Partition No:%d", partition_no); + + if ((reg_value & SCM_STATUS_SRS_MASK) != SCM_STATUS_SRS_READY) + break; + } - if ((reg_value & reg_mask) != reg_mask) { - printk(KERN_ERR "FAILED TO ACQUIRE IRAM PARTITION\n"); + /*Check all expected partitions released */ + reg_value = __raw_readl(scc_base + SCM_PART_OWNERS_REG); + if ((reg_value & reg_mask) != 0) { + printk(KERN_ERR "FAILED TO RELEASE IRAM PARTITION\n"); iounmap(scm_ram_base); iounmap(scc_base); return; } - - for (partition_no = 0; partition_no < iram_partitions; partition_no++) { + reg_mask = 0; + scm_rd_timeout = 0; + /* Allocate remaining partitions for general use */ + for (partition_no = 0; partition_no < scc_partno; partition_no++) { + /* Supervisor mode claims a partition for it's own use + by writing zero to SMID register.*/ + __raw_writel(0, scc_base + (SCM_SMID0_REG + 8 * partition_no)); + + /* Wait for any zeroization to complete */ + getnstimeofday(&stime); + do { + reg_value = __raw_readl(scc_base + SCM_STATUS_REG); + getnstimeofday(&curtime); + if (curtime.tv_nsec > stime.tv_nsec) + scm_rd_timeout = curtime.tv_nsec - + stime.tv_nsec; + else{ + /*Converted second to nanosecond and add to + nsec when current nanosec is less than + start time nanosec.*/ + cur_ns = (curtime.tv_sec * SEC_TO_NANOSEC) + + curtime.tv_nsec; + start_ns = (stime.tv_sec * SEC_TO_NANOSEC) + + stime.tv_nsec; + scm_rd_timeout = cur_ns - start_ns; + } + } while (((reg_value & SCM_STATUS_SRS_MASK) != + SCM_STATUS_SRS_READY) && ((reg_value & SCM_STATUS_SRS_MASK) != + SCM_STATUS_SRS_FAIL) && (scm_rd_timeout <= SCM_RD_DELAY)); + + if (scm_rd_timeout > SCM_RD_DELAY) + printk(KERN_ERR "SCM Status Register Read timeout" + "for Partition No:%d", partition_no); + + if ((reg_value & SCM_STATUS_SRS_MASK) != SCM_STATUS_SRS_READY) + break; + /* Set UMID=0 and permissions for universal data + read/write access */ MAP_base = scm_ram_base + (partition_no * 0x2000); UMID_base = (uint8_t *) MAP_base + 0x10; - for (i = 0; i < 16; i++) UMID_base[i] = 0; - MAP_base[0] = SCM_PERM_NO_ZEROIZE | SCM_PERM_HD_SUP_DISABLE | - SCM_PERM_HD_READ | SCM_PERM_HD_WRITE | SCM_PERM_HD_EXECUTE | - SCM_PERM_TH_READ | SCM_PERM_TH_WRITE ; + MAP_base[0] = (SCM_PERM_NO_ZEROIZE | SCM_PERM_HD_SUP_DISABLE | + SCM_PERM_HD_READ | SCM_PERM_HD_WRITE | + SCM_PERM_HD_EXECUTE | SCM_PERM_TH_READ | + SCM_PERM_TH_WRITE); + reg_mask |= (3 << (2 * (partition_no))); } - /* Freeing 2 partitions for SCC2 */ - scc_partno = iram_partitions - (SCC_IRAM_SIZE / SZ_8K); - for (partition_no = scc_partno; partition_no < iram_partitions; - partition_no++) { - reg_value = ((partition_no << SCM_ZCMD_PART_SHIFT) & - SCM_ZCMD_PART_MASK) | ((0x03 << - SCM_ZCMD_CCMD_SHIFT) - & SCM_ZCMD_CCMD_MASK); - __raw_writel(reg_value, scc_base + SCM_ZCMD_REG); - msleep(1); - while ((__raw_readl(scc_base + SCM_STATUS_REG) & - SCM_STATUS_SRS_READY) != SCM_STATUS_SRS_READY) ; + /* Check all expected partitions allocated */ + reg_value = __raw_readl(scc_base + SCM_PART_OWNERS_REG); + if ((reg_value & reg_mask) != reg_mask) { + printk(KERN_ERR "FAILED TO ACQUIRE IRAM PARTITION\n"); + iounmap(scm_ram_base); + iounmap(scc_base); + return; } + iounmap(scm_ram_base); iounmap(scc_base); printk(KERN_INFO "IRAM READY\n"); diff --git a/include/linux/mxc_scc2_driver.h b/include/linux/mxc_scc2_driver.h index f8a24f278b1c..5f84545bc0d0 100644 --- a/include/linux/mxc_scc2_driver.h +++ b/include/linux/mxc_scc2_driver.h @@ -737,6 +737,8 @@ scc_verify_slot_access(uint64_t owner_id, uint32_t slot, uint32_t access_len);*/ #define SCM_STATUS_SRS_ZDONE2 0x7 /**< Zeroize Done, Cipher Busy */ #define SCM_STATUS_SRS_CDONE2 0x8 /**< Cipher Done, Zeroize Busy */ #define SCM_STATUS_SRS_ADONE 0xD /**< All Done */ +#define SCM_STATUS_SRS_FAIL 0xF /**< Fail State */ + /* Format of the SCM VERSION ID REGISTER */ #define SCM_VER_BPP_MASK 0xFF000000 /**< Bytes Per Partition Mask */ |