aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mtd/onenand/onenand_base.c292
1 files changed, 261 insertions, 31 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/**