8add1f7795326e8b4135538fee26bca6be984bd1
[openwrt/staging/rmilecki.git] / target / linux / generic / pending-5.4 / 481-mtd-spi-nor-rework-broken-flash-reset-support.patch
1 From ea92cbb50a78404e29de2cc3999a240615ffb1c8 Mon Sep 17 00:00:00 2001
2 From: Chuanhong Guo <gch981213@gmail.com>
3 Date: Mon, 6 Apr 2020 17:58:48 +0800
4 Subject: [PATCH] mtd: spi-nor: rework broken-flash-reset support
5
6 Instead of resetting flash to 3B address on remove hook, this
7 implementation only enters 4B mode when needed, which prevents
8 more unexpected reboot stuck. This implementation makes it only
9 break when a kernel panic happens during flash operation on 16M+
10 areas.
11 *OpenWrt only*: silent broken-flash-reset warning. We are not dealing
12 with vendors and it's unpleasant for users to se that unnecessary
13 and long WARN_ON print.
14
15 Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
16 ---
17 drivers/mtd/spi-nor/spi-nor.c | 52 +++++++++++++++++++++++++++++++++--
18 1 file changed, 49 insertions(+), 3 deletions(-)
19
20 --- a/drivers/mtd/spi-nor/spi-nor.c
21 +++ b/drivers/mtd/spi-nor/spi-nor.c
22 @@ -616,6 +616,22 @@ static void spi_nor_set_4byte_opcodes(st
23 }
24 }
25
26 +static int spi_nor_check_set_addr_width(struct spi_nor *nor, loff_t addr)
27 +{
28 + u8 addr_width;
29 +
30 + if ((nor->flags & (SNOR_F_4B_OPCODES | SNOR_F_BROKEN_RESET)) !=
31 + SNOR_F_BROKEN_RESET)
32 + return 0;
33 +
34 + addr_width = addr & 0xff000000 ? 4 : 3;
35 + if (nor->addr_width == addr_width)
36 + return 0;
37 +
38 + nor->addr_width = addr_width;
39 + return nor->params.set_4byte(nor, addr_width == 4);
40 +}
41 +
42 static int macronix_set_4byte(struct spi_nor *nor, bool enable)
43 {
44 if (nor->spimem) {
45 @@ -1259,6 +1275,10 @@ static int spi_nor_erase(struct mtd_info
46 if (ret)
47 return ret;
48
49 + ret = spi_nor_check_set_addr_width(nor, instr->addr + instr->len);
50 + if (ret < 0)
51 + return ret;
52 +
53 /* whole-chip erase? */
54 if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {
55 unsigned long timeout;
56 @@ -1315,6 +1335,7 @@ static int spi_nor_erase(struct mtd_info
57 write_disable(nor);
58
59 erase_err:
60 + spi_nor_check_set_addr_width(nor, 0);
61 spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
62
63 return ret;
64 @@ -1621,7 +1642,9 @@ static int spi_nor_lock(struct mtd_info
65 if (ret)
66 return ret;
67
68 + spi_nor_check_set_addr_width(nor, ofs + len);
69 ret = nor->params.locking_ops->lock(nor, ofs, len);
70 + spi_nor_check_set_addr_width(nor, 0);
71
72 spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_UNLOCK);
73 return ret;
74 @@ -1636,7 +1659,9 @@ static int spi_nor_unlock(struct mtd_inf
75 if (ret)
76 return ret;
77
78 + spi_nor_check_set_addr_width(nor, ofs + len);
79 ret = nor->params.locking_ops->unlock(nor, ofs, len);
80 + spi_nor_check_set_addr_width(nor, 0);
81
82 spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK);
83 return ret;
84 @@ -1651,7 +1676,9 @@ static int spi_nor_is_locked(struct mtd_
85 if (ret)
86 return ret;
87
88 + spi_nor_check_set_addr_width(nor, ofs + len);
89 ret = nor->params.locking_ops->is_locked(nor, ofs, len);
90 + spi_nor_check_set_addr_width(nor, 0);
91
92 spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK);
93 return ret;
94 @@ -2557,6 +2584,10 @@ static int spi_nor_read(struct mtd_info
95 if (ret)
96 return ret;
97
98 + ret = spi_nor_check_set_addr_width(nor, from + len);
99 + if (ret < 0)
100 + return ret;
101 +
102 while (len) {
103 loff_t addr = from;
104
105 @@ -2580,6 +2611,7 @@ static int spi_nor_read(struct mtd_info
106 ret = 0;
107
108 read_err:
109 + spi_nor_check_set_addr_width(nor, 0);
110 spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ);
111 return ret;
112 }
113 @@ -2597,6 +2629,10 @@ static int sst_write(struct mtd_info *mt
114 if (ret)
115 return ret;
116
117 + ret = spi_nor_check_set_addr_width(nor, to + len);
118 + if (ret < 0)
119 + return ret;
120 +
121 write_enable(nor);
122
123 nor->sst_write_second = false;
124 @@ -2659,6 +2695,7 @@ static int sst_write(struct mtd_info *mt
125 }
126 sst_write_err:
127 *retlen += actual;
128 + spi_nor_check_set_addr_width(nor, 0);
129 spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
130 return ret;
131 }
132 @@ -2681,6 +2718,10 @@ static int spi_nor_write(struct mtd_info
133 if (ret)
134 return ret;
135
136 + ret = spi_nor_check_set_addr_width(nor, to + len);
137 + if (ret < 0)
138 + return ret;
139 +
140 for (i = 0; i < len; ) {
141 ssize_t written;
142 loff_t addr = to + i;
143 @@ -2720,6 +2761,7 @@ static int spi_nor_write(struct mtd_info
144 }
145
146 write_err:
147 + spi_nor_check_set_addr_width(nor, 0);
148 spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
149 return ret;
150 }
151 @@ -4725,9 +4767,13 @@ static int spi_nor_init(struct spi_nor *
152 * reboots (e.g., crashes). Warn the user (or hopefully, system
153 * designer) that this is bad.
154 */
155 - WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET,
156 - "enabling reset hack; may not recover from unexpected reboots\n");
157 - nor->params.set_4byte(nor, true);
158 + if (nor->flags & SNOR_F_BROKEN_RESET) {
159 + dev_warn(nor->dev,
160 + "enabling reset hack; may not recover from unexpected reboots\n");
161 + nor->addr_width = 3;
162 + } else {
163 + nor->params.set_4byte(nor, true);
164 + }
165 }
166
167 return 0;