add preliminary 3.14 support
[openwrt.git] / target / linux / generic / patches-3.14 / 411-mtd-partial_eraseblock_write.patch
1 --- a/drivers/mtd/mtdpart.c
2 +++ b/drivers/mtd/mtdpart.c
3 @@ -35,6 +35,8 @@
4  #include "mtdcore.h"
5  #include "mtdsplit.h"
6  
7 +#define MTD_ERASE_PARTIAL      0x8000 /* partition only covers parts of an erase block */
8 +
9  /* Our partition linked list */
10  static LIST_HEAD(mtd_partitions);
11  static DEFINE_MUTEX(mtd_partitions_mutex);
12 @@ -231,13 +233,60 @@ static int part_erase(struct mtd_info *m
13         struct mtd_part *part = PART(mtd);
14         int ret;
15  
16 +
17 +       instr->partial_start = false;
18 +       if (mtd->flags & MTD_ERASE_PARTIAL) {
19 +               size_t readlen = 0;
20 +               u64 mtd_ofs;
21 +
22 +               instr->erase_buf = kmalloc(part->master->erasesize, GFP_ATOMIC);
23 +               if (!instr->erase_buf)
24 +                       return -ENOMEM;
25 +
26 +               mtd_ofs = part->offset + instr->addr;
27 +               instr->erase_buf_ofs = do_div(mtd_ofs, part->master->erasesize);
28 +
29 +               if (instr->erase_buf_ofs > 0) {
30 +                       instr->addr -= instr->erase_buf_ofs;
31 +                       ret = mtd_read(part->master,
32 +                               instr->addr + part->offset,
33 +                               part->master->erasesize,
34 +                               &readlen, instr->erase_buf);
35 +
36 +                       instr->partial_start = true;
37 +               } else {
38 +                       mtd_ofs = part->offset + part->mtd.size;
39 +                       instr->erase_buf_ofs = part->master->erasesize -
40 +                               do_div(mtd_ofs, part->master->erasesize);
41 +
42 +                       if (instr->erase_buf_ofs > 0) {
43 +                               instr->len += instr->erase_buf_ofs;
44 +                               ret = mtd_read(part->master,
45 +                                       part->offset + instr->addr +
46 +                                       instr->len - part->master->erasesize,
47 +                                       part->master->erasesize, &readlen,
48 +                                       instr->erase_buf);
49 +                       } else {
50 +                               ret = 0;
51 +                       }
52 +               }
53 +               if (ret < 0) {
54 +                       kfree(instr->erase_buf);
55 +                       return ret;
56 +               }
57 +
58 +       }
59 +
60         instr->addr += part->offset;
61         ret = part->master->_erase(part->master, instr);
62         if (ret) {
63                 if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
64                         instr->fail_addr -= part->offset;
65                 instr->addr -= part->offset;
66 +               if (mtd->flags & MTD_ERASE_PARTIAL)
67 +                       kfree(instr->erase_buf);
68         }
69 +
70         return ret;
71  }
72  
73 @@ -245,7 +294,25 @@ void mtd_erase_callback(struct erase_inf
74  {
75         if (instr->mtd->_erase == part_erase) {
76                 struct mtd_part *part = PART(instr->mtd);
77 +               size_t wrlen = 0;
78  
79 +               if (instr->mtd->flags & MTD_ERASE_PARTIAL) {
80 +                       if (instr->partial_start) {
81 +                               part->master->_write(part->master,
82 +                                       instr->addr, instr->erase_buf_ofs,
83 +                                       &wrlen, instr->erase_buf);
84 +                               instr->addr += instr->erase_buf_ofs;
85 +                       } else {
86 +                               instr->len -= instr->erase_buf_ofs;
87 +                               part->master->_write(part->master,
88 +                                       instr->addr + instr->len,
89 +                                       instr->erase_buf_ofs, &wrlen,
90 +                                       instr->erase_buf +
91 +                                       part->master->erasesize -
92 +                                       instr->erase_buf_ofs);
93 +                       }
94 +                       kfree(instr->erase_buf);
95 +               }
96                 if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
97                         instr->fail_addr -= part->offset;
98                 instr->addr -= part->offset;
99 @@ -503,18 +570,24 @@ static struct mtd_part *allocate_partiti
100         if ((slave->mtd.flags & MTD_WRITEABLE) &&
101             mtd_mod_by_eb(slave->offset, &slave->mtd)) {
102                 /* Doesn't start on a boundary of major erase size */
103 -               /* FIXME: Let it be writable if it is on a boundary of
104 -                * _minor_ erase size though */
105 -               slave->mtd.flags &= ~MTD_WRITEABLE;
106 -               printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
107 -                       part->name);
108 +               slave->mtd.flags |= MTD_ERASE_PARTIAL;
109 +               if (((u32) slave->mtd.size) > master->erasesize)
110 +                       slave->mtd.flags &= ~MTD_WRITEABLE;
111 +               else
112 +                       slave->mtd.erasesize = slave->mtd.size;
113         }
114         if ((slave->mtd.flags & MTD_WRITEABLE) &&
115 -           mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) {
116 -               slave->mtd.flags &= ~MTD_WRITEABLE;
117 -               printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
118 -                       part->name);
119 +           mtd_mod_by_eb(slave->offset + slave->mtd.size, &slave->mtd)) {
120 +               slave->mtd.flags |= MTD_ERASE_PARTIAL;
121 +
122 +               if ((u32) slave->mtd.size > master->erasesize)
123 +                       slave->mtd.flags &= ~MTD_WRITEABLE;
124 +               else
125 +                       slave->mtd.erasesize = slave->mtd.size;
126         }
127 +       if ((slave->mtd.flags & (MTD_ERASE_PARTIAL|MTD_WRITEABLE)) == MTD_ERASE_PARTIAL)
128 +               printk(KERN_WARNING"mtd: partition \"%s\" must either start or end on erase block boundary or be smaller than an erase block -- forcing read-only\n",
129 +                               part->name);
130  
131         slave->mtd.ecclayout = master->ecclayout;
132         slave->mtd.ecc_step_size = master->ecc_step_size;
133 --- a/include/linux/mtd/mtd.h
134 +++ b/include/linux/mtd/mtd.h
135 @@ -55,6 +55,10 @@ struct erase_info {
136         u_long priv;
137         u_char state;
138         struct erase_info *next;
139 +
140 +       u8 *erase_buf;
141 +       u32 erase_buf_ofs;
142 +       bool partial_start;
143  };
144  
145  struct mtd_erase_region_info {