OpenWrt – Diff between revs 2 and 3
?pathlinks?
Rev 2 | Rev 3 | |||
---|---|---|---|---|
1 | From 61cba34bd6c1bddfc38f94cc3f80bdfefcc3393b Mon Sep 17 00:00:00 2001 |
1 | From 61cba34bd6c1bddfc38f94cc3f80bdfefcc3393b Mon Sep 17 00:00:00 2001 |
|
2 | From: Ricardo Ribalda <ricardo.ribalda@gmail.com> |
2 | From: Ricardo Ribalda <ricardo.ribalda@gmail.com> |
|
3 | Date: Fri, 2 Dec 2016 12:31:44 +0100 |
3 | Date: Fri, 2 Dec 2016 12:31:44 +0100 |
|
4 | Subject: [PATCH] mtd: spi-nor: Add support for S3AN spi-nor devices |
4 | Subject: [PATCH] mtd: spi-nor: Add support for S3AN spi-nor devices |
|
5 | |
5 | |
|
6 | Xilinx Spartan-3AN FPGAs contain an In-System Flash where they keep |
6 | Xilinx Spartan-3AN FPGAs contain an In-System Flash where they keep |
|
7 | their configuration data and (optionally) some user data. |
7 | their configuration data and (optionally) some user data. |
|
8 | |
8 | |
|
9 | The protocol of this flash follows most of the spi-nor standard. With |
9 | The protocol of this flash follows most of the spi-nor standard. With |
|
10 | the following differences: |
10 | the following differences: |
|
11 | |
11 | |
|
12 | - Page size might not be a power of two. |
12 | - Page size might not be a power of two. |
|
13 | - The address calculation (default addressing mode). |
13 | - The address calculation (default addressing mode). |
|
14 | - The spi nor commands used. |
14 | - The spi nor commands used. |
|
15 | |
15 | |
|
16 | Protocol is described on Xilinx User Guide UG333 |
16 | Protocol is described on Xilinx User Guide UG333 |
|
17 | |
17 | |
|
18 | Signed-off-by: Ricardo Ribalda Delgado <ricardo.ribalda@gmail.com> |
18 | Signed-off-by: Ricardo Ribalda Delgado <ricardo.ribalda@gmail.com> |
|
19 | Cc: Boris Brezillon <boris.brezillon@free-electrons.com> |
19 | Cc: Boris Brezillon <boris.brezillon@free-electrons.com> |
|
20 | Cc: Brian Norris <computersforpeace@gmail.com> |
20 | Cc: Brian Norris <computersforpeace@gmail.com> |
|
21 | Cc: Marek Vasut <marek.vasut@gmail.com> |
21 | Cc: Marek Vasut <marek.vasut@gmail.com> |
|
22 | Reviewed-by: Marek Vasut <marek.vasut@gmail.com> |
22 | Reviewed-by: Marek Vasut <marek.vasut@gmail.com> |
|
23 | Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com> |
23 | Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com> |
|
24 | --- |
24 | --- |
|
25 | drivers/mtd/spi-nor/spi-nor.c | 154 ++++++++++++++++++++++++++++++++++++++++-- |
25 | drivers/mtd/spi-nor/spi-nor.c | 154 ++++++++++++++++++++++++++++++++++++++++-- |
|
26 | include/linux/mtd/spi-nor.h | 12 ++++ |
26 | include/linux/mtd/spi-nor.h | 12 ++++ |
|
27 | 2 files changed, 161 insertions(+), 5 deletions(-) |
27 | 2 files changed, 161 insertions(+), 5 deletions(-) |
|
28 | |
28 | |
|
29 | --- a/drivers/mtd/spi-nor/spi-nor.c |
29 | --- a/drivers/mtd/spi-nor/spi-nor.c |
|
30 | +++ b/drivers/mtd/spi-nor/spi-nor.c |
30 | +++ b/drivers/mtd/spi-nor/spi-nor.c |
|
31 | @@ -75,6 +75,12 @@ struct flash_info { |
31 | @@ -75,6 +75,12 @@ struct flash_info { |
|
32 | * bit. Must be used with |
32 | * bit. Must be used with |
|
33 | * SPI_NOR_HAS_LOCK. |
33 | * SPI_NOR_HAS_LOCK. |
|
34 | */ |
34 | */ |
|
35 | +#define SPI_S3AN BIT(10) /* |
35 | +#define SPI_S3AN BIT(10) /* |
|
36 | + * Xilinx Spartan 3AN In-System Flash |
36 | + * Xilinx Spartan 3AN In-System Flash |
|
37 | + * (MFR cannot be used for probing |
37 | + * (MFR cannot be used for probing |
|
38 | + * because it has the same value as |
38 | + * because it has the same value as |
|
39 | + * ATMEL flashes) |
39 | + * ATMEL flashes) |
|
40 | + */ |
40 | + */ |
|
41 | }; |
41 | }; |
|
42 | |
42 | |
|
43 | #define JEDEC_MFR(info) ((info)->id[0]) |
43 | #define JEDEC_MFR(info) ((info)->id[0]) |
|
44 | @@ -217,6 +223,21 @@ static inline int set_4byte(struct spi_n |
44 | @@ -217,6 +223,21 @@ static inline int set_4byte(struct spi_n |
|
45 | return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1); |
45 | return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1); |
|
46 | } |
46 | } |
|
47 | } |
47 | } |
|
48 | + |
48 | + |
|
49 | +static int s3an_sr_ready(struct spi_nor *nor) |
49 | +static int s3an_sr_ready(struct spi_nor *nor) |
|
50 | +{ |
50 | +{ |
|
51 | + int ret; |
51 | + int ret; |
|
52 | + u8 val; |
52 | + u8 val; |
|
53 | + |
53 | + |
|
54 | + ret = nor->read_reg(nor, SPINOR_OP_XRDSR, &val, 1); |
54 | + ret = nor->read_reg(nor, SPINOR_OP_XRDSR, &val, 1); |
|
55 | + if (ret < 0) { |
55 | + if (ret < 0) { |
|
56 | + dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret); |
56 | + dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret); |
|
57 | + return ret; |
57 | + return ret; |
|
58 | + } |
58 | + } |
|
59 | + |
59 | + |
|
60 | + return !!(val & XSR_RDY); |
60 | + return !!(val & XSR_RDY); |
|
61 | +} |
61 | +} |
|
62 | + |
62 | + |
|
63 | static inline int spi_nor_sr_ready(struct spi_nor *nor) |
63 | static inline int spi_nor_sr_ready(struct spi_nor *nor) |
|
64 | { |
64 | { |
|
65 | int sr = read_sr(nor); |
65 | int sr = read_sr(nor); |
|
66 | @@ -238,7 +259,11 @@ static inline int spi_nor_fsr_ready(stru |
66 | @@ -238,7 +259,11 @@ static inline int spi_nor_fsr_ready(stru |
|
67 | static int spi_nor_ready(struct spi_nor *nor) |
67 | static int spi_nor_ready(struct spi_nor *nor) |
|
68 | { |
68 | { |
|
69 | int sr, fsr; |
69 | int sr, fsr; |
|
70 | - sr = spi_nor_sr_ready(nor); |
70 | - sr = spi_nor_sr_ready(nor); |
|
71 | + |
71 | + |
|
72 | + if (nor->flags & SNOR_F_READY_XSR_RDY) |
72 | + if (nor->flags & SNOR_F_READY_XSR_RDY) |
|
73 | + sr = s3an_sr_ready(nor); |
73 | + sr = s3an_sr_ready(nor); |
|
74 | + else |
74 | + else |
|
75 | + sr = spi_nor_sr_ready(nor); |
75 | + sr = spi_nor_sr_ready(nor); |
|
76 | if (sr < 0) |
76 | if (sr < 0) |
|
77 | return sr; |
77 | return sr; |
|
78 | fsr = nor->flags & SNOR_F_USE_FSR ? spi_nor_fsr_ready(nor) : 1; |
78 | fsr = nor->flags & SNOR_F_USE_FSR ? spi_nor_fsr_ready(nor) : 1; |
|
79 | @@ -320,6 +345,24 @@ static void spi_nor_unlock_and_unprep(st |
79 | @@ -320,6 +345,24 @@ static void spi_nor_unlock_and_unprep(st |
|
80 | } |
80 | } |
|
81 | |
81 | |
|
82 | /* |
82 | /* |
|
83 | + * This code converts an address to the Default Address Mode, that has non |
83 | + * This code converts an address to the Default Address Mode, that has non |
|
84 | + * power of two page sizes. We must support this mode because it is the default |
84 | + * power of two page sizes. We must support this mode because it is the default |
|
85 | + * mode supported by Xilinx tools, it can access the whole flash area and |
85 | + * mode supported by Xilinx tools, it can access the whole flash area and |
|
86 | + * changing over to the Power-of-two mode is irreversible and corrupts the |
86 | + * changing over to the Power-of-two mode is irreversible and corrupts the |
|
87 | + * original data. |
87 | + * original data. |
|
88 | + * Addr can safely be unsigned int, the biggest S3AN device is smaller than |
88 | + * Addr can safely be unsigned int, the biggest S3AN device is smaller than |
|
89 | + * 4 MiB. |
89 | + * 4 MiB. |
|
90 | + */ |
90 | + */ |
|
91 | +static loff_t spi_nor_s3an_addr_convert(struct spi_nor *nor, unsigned int addr) |
91 | +static loff_t spi_nor_s3an_addr_convert(struct spi_nor *nor, unsigned int addr) |
|
92 | +{ |
92 | +{ |
|
93 | + unsigned int offset = addr; |
93 | + unsigned int offset = addr; |
|
94 | + |
94 | + |
|
95 | + offset %= nor->page_size; |
95 | + offset %= nor->page_size; |
|
96 | + |
96 | + |
|
97 | + return ((addr - offset) << 1) | offset; |
97 | + return ((addr - offset) << 1) | offset; |
|
98 | +} |
98 | +} |
|
99 | + |
99 | + |
|
100 | +/* |
100 | +/* |
|
101 | * Initiate the erasure of a single sector |
101 | * Initiate the erasure of a single sector |
|
102 | */ |
102 | */ |
|
103 | static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) |
103 | static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) |
|
104 | @@ -327,6 +370,9 @@ static int spi_nor_erase_sector(struct s |
104 | @@ -327,6 +370,9 @@ static int spi_nor_erase_sector(struct s |
|
105 | u8 buf[SPI_NOR_MAX_ADDR_WIDTH]; |
105 | u8 buf[SPI_NOR_MAX_ADDR_WIDTH]; |
|
106 | int i; |
106 | int i; |
|
107 | |
107 | |
|
108 | + if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT) |
108 | + if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT) |
|
109 | + addr = spi_nor_s3an_addr_convert(nor, addr); |
109 | + addr = spi_nor_s3an_addr_convert(nor, addr); |
|
110 | + |
110 | + |
|
111 | if (nor->erase) |
111 | if (nor->erase) |
|
112 | return nor->erase(nor, addr); |
112 | return nor->erase(nor, addr); |
|
113 | |
113 | |
|
114 | @@ -368,7 +414,7 @@ static int spi_nor_erase(struct mtd_info |
114 | @@ -368,7 +414,7 @@ static int spi_nor_erase(struct mtd_info |
|
115 | return ret; |
115 | return ret; |
|
116 | |
116 | |
|
117 | /* whole-chip erase? */ |
117 | /* whole-chip erase? */ |
|
118 | - if (len == mtd->size) { |
118 | - if (len == mtd->size) { |
|
119 | + if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) { |
119 | + if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) { |
|
120 | unsigned long timeout; |
120 | unsigned long timeout; |
|
121 | |
121 | |
|
122 | write_enable(nor); |
122 | write_enable(nor); |
|
123 | @@ -782,6 +828,19 @@ static int spi_nor_is_locked(struct mtd_ |
123 | @@ -782,6 +828,19 @@ static int spi_nor_is_locked(struct mtd_ |
|
124 | .addr_width = (_addr_width), \ |
124 | .addr_width = (_addr_width), \ |
|
125 | .flags = (_flags), |
125 | .flags = (_flags), |
|
126 | |
126 | |
|
127 | +#define S3AN_INFO(_jedec_id, _n_sectors, _page_size) \ |
127 | +#define S3AN_INFO(_jedec_id, _n_sectors, _page_size) \ |
|
128 | + .id = { \ |
128 | + .id = { \ |
|
129 | + ((_jedec_id) >> 16) & 0xff, \ |
129 | + ((_jedec_id) >> 16) & 0xff, \ |
|
130 | + ((_jedec_id) >> 8) & 0xff, \ |
130 | + ((_jedec_id) >> 8) & 0xff, \ |
|
131 | + (_jedec_id) & 0xff \ |
131 | + (_jedec_id) & 0xff \ |
|
132 | + }, \ |
132 | + }, \ |
|
133 | + .id_len = 3, \ |
133 | + .id_len = 3, \ |
|
134 | + .sector_size = (8*_page_size), \ |
134 | + .sector_size = (8*_page_size), \ |
|
135 | + .n_sectors = (_n_sectors), \ |
135 | + .n_sectors = (_n_sectors), \ |
|
136 | + .page_size = _page_size, \ |
136 | + .page_size = _page_size, \ |
|
137 | + .addr_width = 3, \ |
137 | + .addr_width = 3, \ |
|
138 | + .flags = SPI_NOR_NO_FR | SPI_S3AN, |
138 | + .flags = SPI_NOR_NO_FR | SPI_S3AN, |
|
139 | + |
139 | + |
|
140 | /* NOTE: double check command sets and memory organization when you add |
140 | /* NOTE: double check command sets and memory organization when you add |
|
141 | * more nor chips. This current list focusses on newer chips, which |
141 | * more nor chips. This current list focusses on newer chips, which |
|
142 | * have been converging on command sets which including JEDEC ID. |
142 | * have been converging on command sets which including JEDEC ID. |
|
143 | @@ -1020,6 +1079,13 @@ static const struct flash_info spi_nor_i |
143 | @@ -1014,6 +1073,13 @@ static const struct flash_info spi_nor_i |
|
144 | { "cat25c09", CAT25_INFO( 128, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, |
144 | { "cat25c09", CAT25_INFO( 128, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, |
|
145 | { "cat25c17", CAT25_INFO( 256, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, |
145 | { "cat25c17", CAT25_INFO( 256, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, |
|
146 | { "cat25128", CAT25_INFO(2048, 8, 64, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, |
146 | { "cat25128", CAT25_INFO(2048, 8, 64, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, |
|
147 | + |
147 | + |
|
148 | + /* Xilinx S3AN Internal Flash */ |
148 | + /* Xilinx S3AN Internal Flash */ |
|
149 | + { "3S50AN", S3AN_INFO(0x1f2200, 64, 264) }, |
149 | + { "3S50AN", S3AN_INFO(0x1f2200, 64, 264) }, |
|
150 | + { "3S200AN", S3AN_INFO(0x1f2400, 256, 264) }, |
150 | + { "3S200AN", S3AN_INFO(0x1f2400, 256, 264) }, |
|
151 | + { "3S400AN", S3AN_INFO(0x1f2400, 256, 264) }, |
151 | + { "3S400AN", S3AN_INFO(0x1f2400, 256, 264) }, |
|
152 | + { "3S700AN", S3AN_INFO(0x1f2500, 512, 264) }, |
152 | + { "3S700AN", S3AN_INFO(0x1f2500, 512, 264) }, |
|
153 | + { "3S1400AN", S3AN_INFO(0x1f2600, 512, 528) }, |
153 | + { "3S1400AN", S3AN_INFO(0x1f2600, 512, 528) }, |
|
154 | { }, |
154 | { }, |
|
155 | }; |
155 | }; |
|
156 | |
156 | |
|
157 | @@ -1060,7 +1126,12 @@ static int spi_nor_read(struct mtd_info |
157 | @@ -1054,7 +1120,12 @@ static int spi_nor_read(struct mtd_info |
|
158 | return ret; |
158 | return ret; |
|
159 | |
159 | |
|
160 | while (len) { |
160 | while (len) { |
|
161 | - ret = nor->read(nor, from, len, buf); |
161 | - ret = nor->read(nor, from, len, buf); |
|
162 | + loff_t addr = from; |
162 | + loff_t addr = from; |
|
163 | + |
163 | + |
|
164 | + if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT) |
164 | + if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT) |
|
165 | + addr = spi_nor_s3an_addr_convert(nor, addr); |
165 | + addr = spi_nor_s3an_addr_convert(nor, addr); |
|
166 | + |
166 | + |
|
167 | + ret = nor->read(nor, addr, len, buf); |
167 | + ret = nor->read(nor, addr, len, buf); |
|
168 | if (ret == 0) { |
168 | if (ret == 0) { |
|
169 | /* We shouldn't see 0-length reads */ |
169 | /* We shouldn't see 0-length reads */ |
|
170 | ret = -EIO; |
170 | ret = -EIO; |
|
171 | @@ -1181,8 +1252,23 @@ static int spi_nor_write(struct mtd_info |
171 | @@ -1175,8 +1246,23 @@ static int spi_nor_write(struct mtd_info |
|
172 | |
172 | |
|
173 | for (i = 0; i < len; ) { |
173 | for (i = 0; i < len; ) { |
|
174 | ssize_t written; |
174 | ssize_t written; |
|
175 | + loff_t addr = to + i; |
175 | + loff_t addr = to + i; |
|
176 | |
176 | |
|
177 | - page_offset = (to + i) & (nor->page_size - 1); |
177 | - page_offset = (to + i) & (nor->page_size - 1); |
|
178 | + /* |
178 | + /* |
|
179 | + * If page_size is a power of two, the offset can be quickly |
179 | + * If page_size is a power of two, the offset can be quickly |
|
180 | + * calculated with an AND operation. On the other cases we |
180 | + * calculated with an AND operation. On the other cases we |
|
181 | + * need to do a modulus operation (more expensive). |
181 | + * need to do a modulus operation (more expensive). |
|
182 | + * Power of two numbers have only one bit set and we can use |
182 | + * Power of two numbers have only one bit set and we can use |
|
183 | + * the instruction hweight32 to detect if we need to do a |
183 | + * the instruction hweight32 to detect if we need to do a |
|
184 | + * modulus (do_div()) or not. |
184 | + * modulus (do_div()) or not. |
|
185 | + */ |
185 | + */ |
|
186 | + if (hweight32(nor->page_size) == 1) { |
186 | + if (hweight32(nor->page_size) == 1) { |
|
187 | + page_offset = addr & (nor->page_size - 1); |
187 | + page_offset = addr & (nor->page_size - 1); |
|
188 | + } else { |
188 | + } else { |
|
189 | + uint64_t aux = addr; |
189 | + uint64_t aux = addr; |
|
190 | + |
190 | + |
|
191 | + page_offset = do_div(aux, nor->page_size); |
191 | + page_offset = do_div(aux, nor->page_size); |
|
192 | + } |
192 | + } |
|
193 | WARN_ONCE(page_offset, |
193 | WARN_ONCE(page_offset, |
|
194 | "Writing at offset %zu into a NOR page. Writing partial pages may decrease reliability and increase wear of NOR flash.", |
194 | "Writing at offset %zu into a NOR page. Writing partial pages may decrease reliability and increase wear of NOR flash.", |
|
195 | page_offset); |
195 | page_offset); |
|
196 | @@ -1190,8 +1276,11 @@ static int spi_nor_write(struct mtd_info |
196 | @@ -1184,8 +1270,11 @@ static int spi_nor_write(struct mtd_info |
|
197 | page_remain = min_t(size_t, |
197 | page_remain = min_t(size_t, |
|
198 | nor->page_size - page_offset, len - i); |
198 | nor->page_size - page_offset, len - i); |
|
199 | |
199 | |
|
200 | + if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT) |
200 | + if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT) |
|
201 | + addr = spi_nor_s3an_addr_convert(nor, addr); |
201 | + addr = spi_nor_s3an_addr_convert(nor, addr); |
|
202 | + |
202 | + |
|
203 | write_enable(nor); |
203 | write_enable(nor); |
|
204 | - ret = nor->write(nor, to + i, page_remain, buf + i); |
204 | - ret = nor->write(nor, to + i, page_remain, buf + i); |
|
205 | + ret = nor->write(nor, addr, page_remain, buf + i); |
205 | + ret = nor->write(nor, addr, page_remain, buf + i); |
|
206 | if (ret < 0) |
206 | if (ret < 0) |
|
207 | goto write_err; |
207 | goto write_err; |
|
208 | written = ret; |
208 | written = ret; |
|
209 | @@ -1325,6 +1414,47 @@ static int spi_nor_check(struct spi_nor |
209 | @@ -1319,6 +1408,47 @@ static int spi_nor_check(struct spi_nor |
|
210 | return 0; |
210 | return 0; |
|
211 | } |
211 | } |
|
212 | |
212 | |
|
213 | +static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor) |
213 | +static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor) |
|
214 | +{ |
214 | +{ |
|
215 | + int ret; |
215 | + int ret; |
|
216 | + u8 val; |
216 | + u8 val; |
|
217 | + |
217 | + |
|
218 | + ret = nor->read_reg(nor, SPINOR_OP_XRDSR, &val, 1); |
218 | + ret = nor->read_reg(nor, SPINOR_OP_XRDSR, &val, 1); |
|
219 | + if (ret < 0) { |
219 | + if (ret < 0) { |
|
220 | + dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret); |
220 | + dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret); |
|
221 | + return ret; |
221 | + return ret; |
|
222 | + } |
222 | + } |
|
223 | + |
223 | + |
|
224 | + nor->erase_opcode = SPINOR_OP_XSE; |
224 | + nor->erase_opcode = SPINOR_OP_XSE; |
|
225 | + nor->program_opcode = SPINOR_OP_XPP; |
225 | + nor->program_opcode = SPINOR_OP_XPP; |
|
226 | + nor->read_opcode = SPINOR_OP_READ; |
226 | + nor->read_opcode = SPINOR_OP_READ; |
|
227 | + nor->flags |= SNOR_F_NO_OP_CHIP_ERASE; |
227 | + nor->flags |= SNOR_F_NO_OP_CHIP_ERASE; |
|
228 | + |
228 | + |
|
229 | + /* |
229 | + /* |
|
230 | + * This flashes have a page size of 264 or 528 bytes (known as |
230 | + * This flashes have a page size of 264 or 528 bytes (known as |
|
231 | + * Default addressing mode). It can be changed to a more standard |
231 | + * Default addressing mode). It can be changed to a more standard |
|
232 | + * Power of two mode where the page size is 256/512. This comes |
232 | + * Power of two mode where the page size is 256/512. This comes |
|
233 | + * with a price: there is 3% less of space, the data is corrupted |
233 | + * with a price: there is 3% less of space, the data is corrupted |
|
234 | + * and the page size cannot be changed back to default addressing |
234 | + * and the page size cannot be changed back to default addressing |
|
235 | + * mode. |
235 | + * mode. |
|
236 | + * |
236 | + * |
|
237 | + * The current addressing mode can be read from the XRDSR register |
237 | + * The current addressing mode can be read from the XRDSR register |
|
238 | + * and should not be changed, because is a destructive operation. |
238 | + * and should not be changed, because is a destructive operation. |
|
239 | + */ |
239 | + */ |
|
240 | + if (val & XSR_PAGESIZE) { |
240 | + if (val & XSR_PAGESIZE) { |
|
241 | + /* Flash in Power of 2 mode */ |
241 | + /* Flash in Power of 2 mode */ |
|
242 | + nor->page_size = (nor->page_size == 264) ? 256 : 512; |
242 | + nor->page_size = (nor->page_size == 264) ? 256 : 512; |
|
243 | + nor->mtd.writebufsize = nor->page_size; |
243 | + nor->mtd.writebufsize = nor->page_size; |
|
244 | + nor->mtd.size = 8 * nor->page_size * info->n_sectors; |
244 | + nor->mtd.size = 8 * nor->page_size * info->n_sectors; |
|
245 | + nor->mtd.erasesize = 8 * nor->page_size; |
245 | + nor->mtd.erasesize = 8 * nor->page_size; |
|
246 | + } else { |
246 | + } else { |
|
247 | + /* Flash in Default addressing mode */ |
247 | + /* Flash in Default addressing mode */ |
|
248 | + nor->flags |= SNOR_F_S3AN_ADDR_DEFAULT; |
248 | + nor->flags |= SNOR_F_S3AN_ADDR_DEFAULT; |
|
249 | + } |
249 | + } |
|
250 | + |
250 | + |
|
251 | + return 0; |
251 | + return 0; |
|
252 | +} |
252 | +} |
|
253 | + |
253 | + |
|
254 | int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) |
254 | int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) |
|
255 | { |
255 | { |
|
256 | const struct flash_info *info = NULL; |
256 | const struct flash_info *info = NULL; |
|
257 | @@ -1373,6 +1503,14 @@ int spi_nor_scan(struct spi_nor *nor, co |
257 | @@ -1367,6 +1497,14 @@ int spi_nor_scan(struct spi_nor *nor, co |
|
258 | mutex_init(&nor->lock); |
258 | mutex_init(&nor->lock); |
|
259 | |
259 | |
|
260 | /* |
260 | /* |
|
261 | + * Make sure the XSR_RDY flag is set before calling |
261 | + * Make sure the XSR_RDY flag is set before calling |
|
262 | + * spi_nor_wait_till_ready(). Xilinx S3AN share MFR |
262 | + * spi_nor_wait_till_ready(). Xilinx S3AN share MFR |
|
263 | + * with Atmel spi-nor |
263 | + * with Atmel spi-nor |
|
264 | + */ |
264 | + */ |
|
265 | + if (info->flags & SPI_S3AN) |
265 | + if (info->flags & SPI_S3AN) |
|
266 | + nor->flags |= SNOR_F_READY_XSR_RDY; |
266 | + nor->flags |= SNOR_F_READY_XSR_RDY; |
|
267 | + |
267 | + |
|
268 | + /* |
268 | + /* |
|
269 | * Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up |
269 | * Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up |
|
270 | * with the software protection bits set |
270 | * with the software protection bits set |
|
271 | */ |
271 | */ |
|
272 | @@ -1530,6 +1668,12 @@ int spi_nor_scan(struct spi_nor *nor, co |
272 | @@ -1524,6 +1662,12 @@ int spi_nor_scan(struct spi_nor *nor, co |
|
273 | |
273 | |
|
274 | nor->read_dummy = spi_nor_read_dummy_cycles(nor); |
274 | nor->read_dummy = spi_nor_read_dummy_cycles(nor); |
|
275 | |
275 | |
|
276 | + if (info->flags & SPI_S3AN) { |
276 | + if (info->flags & SPI_S3AN) { |
|
277 | + ret = s3an_nor_scan(info, nor); |
277 | + ret = s3an_nor_scan(info, nor); |
|
278 | + if (ret) |
278 | + if (ret) |
|
279 | + return ret; |
279 | + return ret; |
|
280 | + } |
280 | + } |
|
281 | + |
281 | + |
|
282 | dev_info(dev, "%s (%lld Kbytes)\n", info->name, |
282 | dev_info(dev, "%s (%lld Kbytes)\n", info->name, |
|
283 | (long long)mtd->size >> 10); |
283 | (long long)mtd->size >> 10); |
|
284 | |
284 | |
|
285 | --- a/include/linux/mtd/spi-nor.h |
285 | --- a/include/linux/mtd/spi-nor.h |
|
286 | +++ b/include/linux/mtd/spi-nor.h |
286 | +++ b/include/linux/mtd/spi-nor.h |
|
287 | @@ -68,6 +68,15 @@ |
287 | @@ -68,6 +68,15 @@ |
|
288 | #define SPINOR_OP_WRDI 0x04 /* Write disable */ |
288 | #define SPINOR_OP_WRDI 0x04 /* Write disable */ |
|
289 | #define SPINOR_OP_AAI_WP 0xad /* Auto address increment word program */ |
289 | #define SPINOR_OP_AAI_WP 0xad /* Auto address increment word program */ |
|
290 | |
290 | |
|
291 | +/* Used for S3AN flashes only */ |
291 | +/* Used for S3AN flashes only */ |
|
292 | +#define SPINOR_OP_XSE 0x50 /* Sector erase */ |
292 | +#define SPINOR_OP_XSE 0x50 /* Sector erase */ |
|
293 | +#define SPINOR_OP_XPP 0x82 /* Page program */ |
293 | +#define SPINOR_OP_XPP 0x82 /* Page program */ |
|
294 | +#define SPINOR_OP_XRDSR 0xd7 /* Read status register */ |
294 | +#define SPINOR_OP_XRDSR 0xd7 /* Read status register */ |
|
295 | + |
295 | + |
|
296 | +#define XSR_PAGESIZE BIT(0) /* Page size in Po2 or Linear */ |
296 | +#define XSR_PAGESIZE BIT(0) /* Page size in Po2 or Linear */ |
|
297 | +#define XSR_RDY BIT(7) /* Ready */ |
297 | +#define XSR_RDY BIT(7) /* Ready */ |
|
298 | + |
298 | + |
|
299 | + |
299 | + |
|
300 | /* Used for Macronix and Winbond flashes. */ |
300 | /* Used for Macronix and Winbond flashes. */ |
|
301 | #define SPINOR_OP_EN4B 0xb7 /* Enter 4-byte mode */ |
301 | #define SPINOR_OP_EN4B 0xb7 /* Enter 4-byte mode */ |
|
302 | #define SPINOR_OP_EX4B 0xe9 /* Exit 4-byte mode */ |
302 | #define SPINOR_OP_EX4B 0xe9 /* Exit 4-byte mode */ |
|
303 | @@ -119,6 +128,9 @@ enum spi_nor_ops { |
303 | @@ -119,6 +128,9 @@ enum spi_nor_ops { |
|
304 | enum spi_nor_option_flags { |
304 | enum spi_nor_option_flags { |
|
305 | SNOR_F_USE_FSR = BIT(0), |
305 | SNOR_F_USE_FSR = BIT(0), |
|
306 | SNOR_F_HAS_SR_TB = BIT(1), |
306 | SNOR_F_HAS_SR_TB = BIT(1), |
|
307 | + SNOR_F_NO_OP_CHIP_ERASE = BIT(2), |
307 | + SNOR_F_NO_OP_CHIP_ERASE = BIT(2), |
|
308 | + SNOR_F_S3AN_ADDR_DEFAULT = BIT(3), |
308 | + SNOR_F_S3AN_ADDR_DEFAULT = BIT(3), |
|
309 | + SNOR_F_READY_XSR_RDY = BIT(4), |
309 | + SNOR_F_READY_XSR_RDY = BIT(4), |
|
310 | }; |
310 | }; |
|
311 | |
311 | |
|
312 | /** |
312 | /** |
|
313 | |
313 | |