kernel: add linux 4.4 support
[openwrt.git] / target / linux / generic / patches-4.4 / 411-mtd-partial_eraseblock_write.patch
1 --- a/drivers/mtd/mtdpart.c
2 +++ b/drivers/mtd/mtdpart.c
3 @@ -37,6 +37,8 @@
4  #include "mtdcore.h"
5  #include "mtdsplit/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 @@ -235,13 +237,61 @@ 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->len += instr->erase_buf_ofs;
37 +                       instr->partial_start = true;
38 +               } else {
39 +                       mtd_ofs = part->offset + part->mtd.size;
40 +                       instr->erase_buf_ofs = part->master->erasesize -
41 +                               do_div(mtd_ofs, part->master->erasesize);
42 +
43 +                       if (instr->erase_buf_ofs > 0) {
44 +                               instr->len += instr->erase_buf_ofs;
45 +                               ret = mtd_read(part->master,
46 +                                       part->offset + instr->addr +
47 +                                       instr->len - part->master->erasesize,
48 +                                       part->master->erasesize, &readlen,
49 +                                       instr->erase_buf);
50 +                       } else {
51 +                               ret = 0;
52 +                       }
53 +               }
54 +               if (ret < 0) {
55 +                       kfree(instr->erase_buf);
56 +                       return ret;
57 +               }
58 +
59 +       }
60 +
61         instr->addr += part->offset;
62         ret = part->master->_erase(part->master, instr);
63         if (ret) {
64                 if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
65                         instr->fail_addr -= part->offset;
66                 instr->addr -= part->offset;
67 +               if (mtd->flags & MTD_ERASE_PARTIAL)
68 +                       kfree(instr->erase_buf);
69         }
70 +
71         return ret;
72  }
73  
74 @@ -249,7 +299,25 @@ void mtd_erase_callback(struct erase_inf
75  {
76         if (instr->mtd->_erase == part_erase) {
77                 struct mtd_part *part = PART(instr->mtd);
78 +               size_t wrlen = 0;
79  
80 +               if (instr->mtd->flags & MTD_ERASE_PARTIAL) {
81 +                       if (instr->partial_start) {
82 +                               part->master->_write(part->master,
83 +                                       instr->addr, instr->erase_buf_ofs,
84 +                                       &wrlen, instr->erase_buf);
85 +                               instr->addr += instr->erase_buf_ofs;
86 +                       } else {
87 +                               instr->len -= instr->erase_buf_ofs;
88 +                               part->master->_write(part->master,
89 +                                       instr->addr + instr->len,
90 +                                       instr->erase_buf_ofs, &wrlen,
91 +                                       instr->erase_buf +
92 +                                       part->master->erasesize -
93 +                                       instr->erase_buf_ofs);
94 +                       }
95 +                       kfree(instr->erase_buf);
96 +               }
97                 if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
98                         instr->fail_addr -= part->offset;
99                 instr->addr -= part->offset;
100 @@ -522,17 +590,20 @@ static struct mtd_part *allocate_partiti
101         if ((slave->mtd.flags & MTD_WRITEABLE) &&
102             mtd_mod_by_eb(slave->offset, &slave->mtd)) {
103                 /* Doesn't start on a boundary of major erase size */
104 -               /* FIXME: Let it be writable if it is on a boundary of
105 -                * _minor_ erase size though */
106 -               slave->mtd.flags &= ~MTD_WRITEABLE;
107 -               printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
108 -                       part->name);
109 +               slave->mtd.flags |= MTD_ERASE_PARTIAL;
110 +               if (((u32) slave->mtd.size) > master->erasesize)
111 +                       slave->mtd.flags &= ~MTD_WRITEABLE;
112 +               else
113 +                       slave->mtd.erasesize = slave->mtd.size;
114         }
115         if ((slave->mtd.flags & MTD_WRITEABLE) &&
116 -           mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) {
117 -               slave->mtd.flags &= ~MTD_WRITEABLE;
118 -               printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
119 -                       part->name);
120 +           mtd_mod_by_eb(slave->offset + slave->mtd.size, &slave->mtd)) {
121 +               slave->mtd.flags |= MTD_ERASE_PARTIAL;
122 +
123 +               if ((u32) slave->mtd.size > master->erasesize)
124 +                       slave->mtd.flags &= ~MTD_WRITEABLE;
125 +               else
126 +                       slave->mtd.erasesize = slave->mtd.size;
127         }
128  
129         slave->mtd.ecclayout = master->ecclayout;
130 --- a/include/linux/mtd/mtd.h
131 +++ b/include/linux/mtd/mtd.h
132 @@ -55,6 +55,10 @@ struct erase_info {
133         u_long priv;
134         u_char state;
135         struct erase_info *next;
136 +
137 +       u8 *erase_buf;
138 +       u32 erase_buf_ofs;
139 +       bool partial_start;
140  };
141  
142  struct mtd_erase_region_info {