aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAmul Kumar Saha <amul.saha@samsung.com>2009-10-21 07:30:05 -0400
committerDavid Woodhouse <David.Woodhouse@intel.com>2009-11-30 04:31:13 -0500
commit3cf602532c535ec655725e9833378e04c9fd7783 (patch)
tree93f8fba910778012b6a5a82cf7beb920e460fe69
parent782e5711d61b2cda45dea447badba3ab07c236f0 (diff)
mtd: OneNAND OTP support rework
What is OTP in OneNAND? The device includes, 1. one block-sized OTP (One Time Programmable) area and 2. user-controlled 1st block OTP(Block 0) that can be used to increase system security or to provide identification capabilities. What is done? In OneNAND, one block of the NAND Array is set aside as an OTP memory area, and 1st Block (Block 0) can be used as OTP area. This area, available to the user, can be configured and locked with secured user information. The OTP block can be read, programmed and locked using the same operations as any other NAND Flash Array memory block. After issuing an OTP-Lock, OTP block cannot be erased. OTP block is fully-guaranteed to be a good block. Why it is done? Locking the 1st Block OTP has the effect of a 'Write-protect' to guard against accidental re-programming of data stored in the 1st block and OTP Block. Which problem it solves? OTP support is provided in the existing implementation of OneNAND/Flex-OneNAND driver, but it is not working with OneNAND devices. Have observed the following in current OTP OneNAND Implmentation, 1. DataSheet specific sequence to lock the OTP Area is not followed. 2. Certain functions are quiet generic to cope with OTP specific activity. This patch re-implements OTP support for OneNAND device. How it is done? For all blocks, 8th word is available to the user. However, in case of OTP Block, 8th word of sector 0, page 0 is reserved as OTP Locking Bit area. Therefore, in case of OTP Block, user usage on this area is prohibited. Condition specific values are entered in the 8th word, sector0, page 0 of the OTP block during the process of issuing an OTP-Lock. The possible conditions are: 1. Only 1st Block Lock 2. Only OTP Block Lock 3. Lock both the 1st Block and the OTP Block What Other feature additions have been done in this patch? This patch adds feature for: 1. Only 1st Block Lock 2. Lock both the 1st Block and the OTP Blocks Re-implemented OTP support for OneNAND Added following features to OneNAND 1. Lock only 1st Block in OneNAND 2. Lock BOTH 1st Block and OTP Block in OneNAND [comments were slightly tweaked by Artem] Signed-off-by: Amul Kumar Saha <amul.saha@samsung.com> Reviewed-by: Adrian Hunter <adrian.hunter@nokia.com> Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
-rw-r--r--drivers/mtd/onenand/onenand_base.c292
-rw-r--r--include/linux/mtd/onenand.h4
2 files changed, 264 insertions, 32 deletions
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 6e250f3a4a16..7bd6ad3ff30a 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -1,17 +1,19 @@
1/* 1/*
2 * linux/drivers/mtd/onenand/onenand_base.c 2 * linux/drivers/mtd/onenand/onenand_base.c
3 * 3 *
4 * Copyright (C) 2005-2007 Samsung Electronics 4 * Copyright © 2005-2009 Samsung Electronics
5 * Copyright © 2007 Nokia Corporation
6 *
5 * Kyungmin Park <kyungmin.park@samsung.com> 7 * Kyungmin Park <kyungmin.park@samsung.com>
6 * 8 *
7 * Credits: 9 * Credits:
8 * Adrian Hunter <ext-adrian.hunter@nokia.com>: 10 * Adrian Hunter <ext-adrian.hunter@nokia.com>:
9 * auto-placement support, read-while load support, various fixes 11 * auto-placement support, read-while load support, various fixes
10 * Copyright (C) Nokia Corporation, 2007
11 * 12 *
12 * Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com> 13 * Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com>
13 * Flex-OneNAND support 14 * Flex-OneNAND support
14 * Copyright (C) Samsung Electronics, 2008 15 * Amul Kumar Saha <amul.saha at samsung.com>
16 * OTP support
15 * 17 *
16 * This program is free software; you can redistribute it and/or modify 18 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License version 2 as 19 * it under the terms of the GNU General Public License version 2 as
@@ -43,6 +45,18 @@ MODULE_PARM_DESC(flex_bdry, "SLC Boundary information for Flex-OneNAND"
43 " : 0->Set boundary in unlocked status" 45 " : 0->Set boundary in unlocked status"
44 " : 1->Set boundary in locked status"); 46 " : 1->Set boundary in locked status");
45 47
48/* Default OneNAND/Flex-OneNAND OTP options*/
49static int otp;
50
51module_param(otp, int, 0400);
52MODULE_PARM_DESC(otp, "Corresponding behaviour of OneNAND in OTP"
53 "Syntax : otp=LOCK_TYPE"
54 "LOCK_TYPE : Keys issued, for specific OTP Lock type"
55 " : 0 -> Default (No Blocks Locked)"
56 " : 1 -> OTP Block lock"
57 " : 2 -> 1st Block lock"
58 " : 3 -> BOTH OTP Block and 1st Block lock");
59
46/** 60/**
47 * onenand_oob_128 - oob info for Flex-Onenand with 4KB page 61 * onenand_oob_128 - oob info for Flex-Onenand with 4KB page
48 * For now, we expose only 64 out of 80 ecc bytes 62 * For now, we expose only 64 out of 80 ecc bytes
@@ -2591,6 +2605,208 @@ static void onenand_unlock_all(struct mtd_info *mtd)
2591 2605
2592#ifdef CONFIG_MTD_ONENAND_OTP 2606#ifdef CONFIG_MTD_ONENAND_OTP
2593 2607
2608/**
2609 * onenand_otp_command - Send OTP specific command to OneNAND device
2610 * @param mtd MTD device structure
2611 * @param cmd the command to be sent
2612 * @param addr offset to read from or write to
2613 * @param len number of bytes to read or write
2614 */
2615static int onenand_otp_command(struct mtd_info *mtd, int cmd, loff_t addr,
2616 size_t len)
2617{
2618 struct onenand_chip *this = mtd->priv;
2619 int value, block, page;
2620
2621 /* Address translation */
2622 switch (cmd) {
2623 case ONENAND_CMD_OTP_ACCESS:
2624 block = (int) (addr >> this->erase_shift);
2625 page = -1;
2626 break;
2627
2628 default:
2629 block = (int) (addr >> this->erase_shift);
2630 page = (int) (addr >> this->page_shift);
2631
2632 if (ONENAND_IS_2PLANE(this)) {
2633 /* Make the even block number */
2634 block &= ~1;
2635 /* Is it the odd plane? */
2636 if (addr & this->writesize)
2637 block++;
2638 page >>= 1;
2639 }
2640 page &= this->page_mask;
2641 break;
2642 }
2643
2644 if (block != -1) {
2645 /* Write 'DFS, FBA' of Flash */
2646 value = onenand_block_address(this, block);
2647 this->write_word(value, this->base +
2648 ONENAND_REG_START_ADDRESS1);
2649 }
2650
2651 if (page != -1) {
2652 /* Now we use page size operation */
2653 int sectors = 4, count = 4;
2654 int dataram;
2655
2656 switch (cmd) {
2657 default:
2658 if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG)
2659 cmd = ONENAND_CMD_2X_PROG;
2660 dataram = ONENAND_CURRENT_BUFFERRAM(this);
2661 break;
2662 }
2663
2664 /* Write 'FPA, FSA' of Flash */
2665 value = onenand_page_address(page, sectors);
2666 this->write_word(value, this->base +
2667 ONENAND_REG_START_ADDRESS8);
2668
2669 /* Write 'BSA, BSC' of DataRAM */
2670 value = onenand_buffer_address(dataram, sectors, count);
2671 this->write_word(value, this->base + ONENAND_REG_START_BUFFER);
2672 }
2673
2674 /* Interrupt clear */
2675 this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT);
2676
2677 /* Write command */
2678 this->write_word(cmd, this->base + ONENAND_REG_COMMAND);
2679
2680 return 0;
2681}
2682
2683/**
2684 * onenand_otp_write_oob_nolock - [Internal] OneNAND write out-of-band, specific to OTP
2685 * @param mtd MTD device structure
2686 * @param to offset to write to
2687 * @param len number of bytes to write
2688 * @param retlen pointer to variable to store the number of written bytes
2689 * @param buf the data to write
2690 *
2691 * OneNAND write out-of-band only for OTP
2692 */
2693static int onenand_otp_write_oob_nolock(struct mtd_info *mtd, loff_t to,
2694 struct mtd_oob_ops *ops)
2695{
2696 struct onenand_chip *this = mtd->priv;
2697 int column, ret = 0, oobsize;
2698 int written = 0;
2699 u_char *oobbuf;
2700 size_t len = ops->ooblen;
2701 const u_char *buf = ops->oobbuf;
2702 int block, value, status;
2703
2704 to += ops->ooboffs;
2705
2706 /* Initialize retlen, in case of early exit */
2707 ops->oobretlen = 0;
2708
2709 oobsize = mtd->oobsize;
2710
2711 column = to & (mtd->oobsize - 1);
2712
2713 oobbuf = this->oob_buf;
2714
2715 /* Loop until all data write */
2716 while (written < len) {
2717 int thislen = min_t(int, oobsize, len - written);
2718
2719 cond_resched();
2720
2721 block = (int) (to >> this->erase_shift);
2722 /*
2723 * Write 'DFS, FBA' of Flash
2724 * Add: F100h DQ=DFS, FBA
2725 */
2726
2727 value = onenand_block_address(this, block);
2728 this->write_word(value, this->base +
2729 ONENAND_REG_START_ADDRESS1);
2730
2731 /*
2732 * Select DataRAM for DDP
2733 * Add: F101h DQ=DBS
2734 */
2735
2736 value = onenand_bufferram_address(this, block);
2737 this->write_word(value, this->base +
2738 ONENAND_REG_START_ADDRESS2);
2739 ONENAND_SET_NEXT_BUFFERRAM(this);
2740
2741 /*
2742 * Enter OTP access mode
2743 */
2744 this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
2745 this->wait(mtd, FL_OTPING);
2746
2747 /* We send data to spare ram with oobsize
2748 * to prevent byte access */
2749 memcpy(oobbuf + column, buf, thislen);
2750
2751 /*
2752 * Write Data into DataRAM
2753 * Add: 8th Word
2754 * in sector0/spare/page0
2755 * DQ=XXFCh
2756 */
2757 this->write_bufferram(mtd, ONENAND_SPARERAM,
2758 oobbuf, 0, mtd->oobsize);
2759
2760 onenand_otp_command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize);
2761 onenand_update_bufferram(mtd, to, 0);
2762 if (ONENAND_IS_2PLANE(this)) {
2763 ONENAND_SET_BUFFERRAM1(this);
2764 onenand_update_bufferram(mtd, to + this->writesize, 0);
2765 }
2766
2767 ret = this->wait(mtd, FL_WRITING);
2768 if (ret) {
2769 printk(KERN_ERR "%s: write failed %d\n", __func__, ret);
2770 break;
2771 }
2772
2773 /* Exit OTP access mode */
2774 this->command(mtd, ONENAND_CMD_RESET, 0, 0);
2775 this->wait(mtd, FL_RESETING);
2776
2777 status = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
2778 status &= 0x60;
2779
2780 if (status == 0x60) {
2781 printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
2782 printk(KERN_DEBUG "1st Block\tLOCKED\n");
2783 printk(KERN_DEBUG "OTP Block\tLOCKED\n");
2784 } else if (status == 0x20) {
2785 printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
2786 printk(KERN_DEBUG "1st Block\tLOCKED\n");
2787 printk(KERN_DEBUG "OTP Block\tUN-LOCKED\n");
2788 } else if (status == 0x40) {
2789 printk(KERN_DEBUG "\nBLOCK\tSTATUS\n");
2790 printk(KERN_DEBUG "1st Block\tUN-LOCKED\n");
2791 printk(KERN_DEBUG "OTP Block\tLOCKED\n");
2792 } else {
2793 printk(KERN_DEBUG "Reboot to check\n");
2794 }
2795
2796 written += thislen;
2797 if (written == len)
2798 break;
2799
2800 to += mtd->writesize;
2801 buf += thislen;
2802 column = 0;
2803 }
2804
2805 ops->oobretlen = written;
2806
2807 return ret;
2808}
2809
2594/* Internal OTP operation */ 2810/* Internal OTP operation */
2595typedef int (*otp_op_t)(struct mtd_info *mtd, loff_t form, size_t len, 2811typedef int (*otp_op_t)(struct mtd_info *mtd, loff_t form, size_t len,
2596 size_t *retlen, u_char *buf); 2812 size_t *retlen, u_char *buf);
@@ -2693,11 +2909,11 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
2693 struct mtd_oob_ops ops; 2909 struct mtd_oob_ops ops;
2694 int ret; 2910 int ret;
2695 2911
2696 /* Enter OTP access mode */
2697 this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
2698 this->wait(mtd, FL_OTPING);
2699
2700 if (FLEXONENAND(this)) { 2912 if (FLEXONENAND(this)) {
2913
2914 /* Enter OTP access mode */
2915 this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
2916 this->wait(mtd, FL_OTPING);
2701 /* 2917 /*
2702 * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of 2918 * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
2703 * main area of page 49. 2919 * main area of page 49.
@@ -2708,19 +2924,19 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
2708 ops.oobbuf = NULL; 2924 ops.oobbuf = NULL;
2709 ret = onenand_write_ops_nolock(mtd, mtd->writesize * 49, &ops); 2925 ret = onenand_write_ops_nolock(mtd, mtd->writesize * 49, &ops);
2710 *retlen = ops.retlen; 2926 *retlen = ops.retlen;
2927
2928 /* Exit OTP access mode */
2929 this->command(mtd, ONENAND_CMD_RESET, 0, 0);
2930 this->wait(mtd, FL_RESETING);
2711 } else { 2931 } else {
2712 ops.mode = MTD_OOB_PLACE; 2932 ops.mode = MTD_OOB_PLACE;
2713 ops.ooblen = len; 2933 ops.ooblen = len;
2714 ops.oobbuf = buf; 2934 ops.oobbuf = buf;
2715 ops.ooboffs = 0; 2935 ops.ooboffs = 0;
2716 ret = onenand_write_oob_nolock(mtd, from, &ops); 2936 ret = onenand_otp_write_oob_nolock(mtd, from, &ops);
2717 *retlen = ops.oobretlen; 2937 *retlen = ops.oobretlen;
2718 } 2938 }
2719 2939
2720 /* Exit OTP access mode */
2721 this->command(mtd, ONENAND_CMD_RESET, 0, 0);
2722 this->wait(mtd, FL_RESETING);
2723
2724 return ret; 2940 return ret;
2725} 2941}
2726 2942
@@ -2751,16 +2967,21 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
2751 if (density < ONENAND_DEVICE_DENSITY_512Mb) 2967 if (density < ONENAND_DEVICE_DENSITY_512Mb)
2752 otp_pages = 20; 2968 otp_pages = 20;
2753 else 2969 else
2754 otp_pages = 10; 2970 otp_pages = 50;
2755 2971
2756 if (mode == MTD_OTP_FACTORY) { 2972 if (mode == MTD_OTP_FACTORY) {
2757 from += mtd->writesize * otp_pages; 2973 from += mtd->writesize * otp_pages;
2758 otp_pages = 64 - otp_pages; 2974 otp_pages = ONENAND_PAGES_PER_BLOCK - otp_pages;
2759 } 2975 }
2760 2976
2761 /* Check User/Factory boundary */ 2977 /* Check User/Factory boundary */
2762 if (((mtd->writesize * otp_pages) - (from + len)) < 0) 2978 if (mode == MTD_OTP_USER) {
2763 return 0; 2979 if (((mtd->writesize * otp_pages) - (from + len)) < 0)
2980 return 0;
2981 } else {
2982 if (((mtd->writesize * otp_pages) - len) < 0)
2983 return 0;
2984 }
2764 2985
2765 onenand_get_device(mtd, FL_OTPING); 2986 onenand_get_device(mtd, FL_OTPING);
2766 while (len > 0 && otp_pages > 0) { 2987 while (len > 0 && otp_pages > 0) {
@@ -2783,13 +3004,12 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
2783 *retlen += sizeof(struct otp_info); 3004 *retlen += sizeof(struct otp_info);
2784 } else { 3005 } else {
2785 size_t tmp_retlen; 3006 size_t tmp_retlen;
2786 int size = len;
2787 3007
2788 ret = action(mtd, from, len, &tmp_retlen, buf); 3008 ret = action(mtd, from, len, &tmp_retlen, buf);
2789 3009
2790 buf += size; 3010 buf += tmp_retlen;
2791 len -= size; 3011 len -= tmp_retlen;
2792 *retlen += size; 3012 *retlen += tmp_retlen;
2793 3013
2794 if (ret) 3014 if (ret)
2795 break; 3015 break;
@@ -2902,21 +3122,11 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
2902 u_char *buf = FLEXONENAND(this) ? this->page_buf : this->oob_buf; 3122 u_char *buf = FLEXONENAND(this) ? this->page_buf : this->oob_buf;
2903 size_t retlen; 3123 size_t retlen;
2904 int ret; 3124 int ret;
3125 unsigned int otp_lock_offset = ONENAND_OTP_LOCK_OFFSET;
2905 3126
2906 memset(buf, 0xff, FLEXONENAND(this) ? this->writesize 3127 memset(buf, 0xff, FLEXONENAND(this) ? this->writesize
2907 : mtd->oobsize); 3128 : mtd->oobsize);
2908 /* 3129 /*
2909 * Note: OTP lock operation
2910 * OTP block : 0xXXFC
2911 * 1st block : 0xXXF3 (If chip support)
2912 * Both : 0xXXF0 (If chip support)
2913 */
2914 if (FLEXONENAND(this))
2915 buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC;
2916 else
2917 buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC;
2918
2919 /*
2920 * Write lock mark to 8th word of sector0 of page0 of the spare0. 3130 * Write lock mark to 8th word of sector0 of page0 of the spare0.
2921 * We write 16 bytes spare area instead of 2 bytes. 3131 * We write 16 bytes spare area instead of 2 bytes.
2922 * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of 3132 * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
@@ -2926,10 +3136,30 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
2926 from = 0; 3136 from = 0;
2927 len = FLEXONENAND(this) ? mtd->writesize : 16; 3137 len = FLEXONENAND(this) ? mtd->writesize : 16;
2928 3138
3139 /*
3140 * Note: OTP lock operation
3141 * OTP block : 0xXXFC XX 1111 1100
3142 * 1st block : 0xXXF3 (If chip support) XX 1111 0011
3143 * Both : 0xXXF0 (If chip support) XX 1111 0000
3144 */
3145 if (FLEXONENAND(this))
3146 otp_lock_offset = FLEXONENAND_OTP_LOCK_OFFSET;
3147
3148 /* ONENAND_OTP_AREA | ONENAND_OTP_BLOCK0 | ONENAND_OTP_AREA_BLOCK0 */
3149 if (otp == 1)
3150 buf[otp_lock_offset] = 0xFC;
3151 else if (otp == 2)
3152 buf[otp_lock_offset] = 0xF3;
3153 else if (otp == 3)
3154 buf[otp_lock_offset] = 0xF0;
3155 else if (otp != 0)
3156 printk(KERN_DEBUG "[OneNAND] Invalid option selected for OTP\n");
3157
2929 ret = onenand_otp_walk(mtd, from, len, &retlen, buf, do_otp_lock, MTD_OTP_USER); 3158 ret = onenand_otp_walk(mtd, from, len, &retlen, buf, do_otp_lock, MTD_OTP_USER);
2930 3159
2931 return ret ? : retlen; 3160 return ret ? : retlen;
2932} 3161}
3162
2933#endif /* CONFIG_MTD_ONENAND_OTP */ 3163#endif /* CONFIG_MTD_ONENAND_OTP */
2934 3164
2935/** 3165/**
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h
index f57e29e17bb0..5509eb06b326 100644
--- a/include/linux/mtd/onenand.h
+++ b/include/linux/mtd/onenand.h
@@ -1,7 +1,7 @@
1/* 1/*
2 * linux/include/linux/mtd/onenand.h 2 * linux/include/linux/mtd/onenand.h
3 * 3 *
4 * Copyright (C) 2005-2007 Samsung Electronics 4 * Copyright © 2005-2009 Samsung Electronics
5 * Kyungmin Park <kyungmin.park@samsung.com> 5 * Kyungmin Park <kyungmin.park@samsung.com>
6 * 6 *
7 * This program is free software; you can redistribute it and/or modify 7 * This program is free software; you can redistribute it and/or modify
@@ -137,6 +137,8 @@ struct onenand_chip {
137/* 137/*
138 * Helper macros 138 * Helper macros
139 */ 139 */
140#define ONENAND_PAGES_PER_BLOCK (1<<6)
141
140#define ONENAND_CURRENT_BUFFERRAM(this) (this->bufferram_index) 142#define ONENAND_CURRENT_BUFFERRAM(this) (this->bufferram_index)
141#define ONENAND_NEXT_BUFFERRAM(this) (this->bufferram_index ^ 1) 143#define ONENAND_NEXT_BUFFERRAM(this) (this->bufferram_index ^ 1)
142#define ONENAND_SET_NEXT_BUFFERRAM(this) (this->bufferram_index ^= 1) 144#define ONENAND_SET_NEXT_BUFFERRAM(this) (this->bufferram_index ^= 1)