Skip to content

Commit 908a999

Browse files
mdmilleriishenki
authored andcommitted
mtd: spi-nor: aspeed-smc: Use io string accessors to fifo
Make and use new routines to access the fifo when in user mode. Instead of using memcpy_fromio, base the new routines on the io port string accessors. When the smc controllers are configured to transfer in user mode, the exact address within the chip select region is ignored and all valid data bytes are sent to the device fifo. It has been determined the reason _memcpy_fromio, an arm specific byte-by-byte variant of memcpy_fromio was required in read_reg, is that the later will stutter when coping from memory, discarding some bytes read, when switching from words to bytes for the final unaligned length. This change includes a hack to make inw/outw work. The default generic IO_SPACE_LIMIT is 0, and twenty bits when PCI is selected. While there is a PCMCIA SOC config to select the full 4GB space it would bring in the PCMCIA core for no use to the ast2400. We want to be part of the generic multi-platform arm kernel and therefore mach/io.h is not going to work. However, the limit is under an ifndef, so define it in this file an check it was not redefine at probe time. To avoid redefinition errors or warnings, guard the setting with CONFIG_ARM for cross-arch compile testing and with a Kconfig dependency for arm cross-compile tests. While some other architecture may not accept unsigned long addresses as ioport cookies, that can be addressed when, if ever, the hardware appears on another arch. While the change to use the designed-for-memory optimised copy template, even reusing the bytes of memcpy, was a recent change and only for little endian arm, designing and proposing a patch will take some time and there could be debate if the routine needs to work on changing memory. Signed-off-by: Milton Miller <[email protected]> Reviewed-by: Andrew Jeffery <[email protected]> Signed-off-by: Joel Stanley <[email protected]>
1 parent 794044d commit 908a999

File tree

2 files changed

+113
-9
lines changed

2 files changed

+113
-9
lines changed

drivers/mtd/spi-nor/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ config ASPEED_FLASH_SPI
4444
tristate "Aspeed flash controllers in SPI mode"
4545
depends on HAS_IOMEM && OF
4646
depends on ARCH_ASPEED || COMPILE_TEST
47+
# IO_SPACE_LIMIT must be equivalent to (~0UL)
48+
depends on !NEED_MACH_IO_H
4749
help
4850
This enables support for the New Static Memory Controller (FMC)
4951
in the AST2400 when attached to SPI nor chips, and support for

drivers/mtd/spi-nor/aspeed-smc.c

Lines changed: 111 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@
88
*
99
*/
1010

11+
/* See comment by aspeed_smc_from_fifo */
12+
#ifdef CONFIG_ARM
13+
#define IO_SPACE_LIMIT (~0UL)
14+
#endif
15+
16+
#include <linux/bug.h>
1117
#include <linux/device.h>
1218
#include <linux/io.h>
1319
#include <linux/module.h>
@@ -19,6 +25,94 @@
1925
#include <linux/of_platform.h>
2026
#include <linux/sysfs.h>
2127

28+
/*
29+
* On the arm architecture, as of Linux version 4.3, memcpy_fromio
30+
* stutters discarding some of the bytes read if the destination is
31+
* unaligned, so we can't use it for reading from a fifo to a buffer
32+
* of unknown alignment. Instead use the ins (l, w, b) family
33+
* to read from the fifo. However, ARM tries to hide io port
34+
* accesses from drivers unless there is a PCMCIA or PCI device, so
35+
* we define the limit before all include files. There is a
36+
* check in probe to make sure this will work, as long as the
37+
* architecture uses an identity iomap.
38+
*/
39+
40+
static void aspeed_smc_from_fifo(void *buf, const void __iomem *iop, size_t len)
41+
{
42+
unsigned long io = (__force unsigned long)iop;
43+
44+
if (!len)
45+
return;
46+
47+
/* Expect a 4 byte input port. Otherwise just read bytes */
48+
if (unlikely(io & 3)) {
49+
insb(io, buf, len);
50+
return;
51+
}
52+
53+
/* Align target to word: first byte then half word */
54+
if ((unsigned long)buf & 1) {
55+
*(u8 *)buf = inb(io);
56+
buf++;
57+
len--;
58+
}
59+
if (((unsigned long)buf & 2) && (len >= 2)) {
60+
*(u16 *)buf = inw(io);
61+
buf += 2;
62+
len -= 2;
63+
}
64+
/* Transfer words, then remaining halfword and remaining byte */
65+
if (len >= 4) {
66+
insl(io, buf, len >> 2);
67+
buf += len & ~3;
68+
}
69+
if (len & 2) {
70+
*(u16 *)buf = inw(io);
71+
buf += 2;
72+
}
73+
if (len & 1) {
74+
*(u8 *)buf = inb(io);
75+
}
76+
}
77+
78+
static void aspeed_smc_to_fifo(void __iomem *iop, const void *buf, size_t len)
79+
{
80+
unsigned long io = (__force unsigned long)iop;
81+
82+
if (!len)
83+
return;
84+
85+
/* Expect a 4 byte output port. Otherwise just write bytes */
86+
if (io & 3) {
87+
outsb(io, buf, len);
88+
return;
89+
}
90+
91+
/* Align target to word: first byte then half word */
92+
if ((unsigned long)buf & 1) {
93+
outb(*(u8 *)buf, io);
94+
buf++;
95+
len--;
96+
}
97+
if (((unsigned long)buf & 2) && (len >= 2)) {
98+
outw(*(u16 *)buf, io);
99+
buf += 2;
100+
len -= 2;
101+
}
102+
/* Transfer words, then remaining halfword and remaining byte */
103+
if (len >= 4) {
104+
outsl(io, buf, len >> 2);
105+
buf += len & ~(size_t)3;
106+
}
107+
if (len & 2) {
108+
outw(*(u16 *)buf, io);
109+
buf += 2;
110+
}
111+
if (len & 1) {
112+
outb(*(u8 *)buf, io);
113+
}
114+
}
115+
22116
enum smc_flash_type {
23117
smc_type_nor = 0, /* controller connected to nor flash */
24118
smc_type_nand = 1, /* controller connected to nand flash */
@@ -165,8 +259,8 @@ static int aspeed_smc_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
165259
struct aspeed_smc_chip *chip = nor->priv;
166260

167261
aspeed_smc_start_user(nor);
168-
writeb(opcode, chip->base);
169-
_memcpy_fromio(buf, chip->base, len);
262+
aspeed_smc_to_fifo(chip->base, &opcode, 1);
263+
aspeed_smc_from_fifo(buf, chip->base, len);
170264
aspeed_smc_stop_user(nor);
171265

172266
return 0;
@@ -178,8 +272,8 @@ static int aspeed_smc_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
178272
struct aspeed_smc_chip *chip = nor->priv;
179273

180274
aspeed_smc_start_user(nor);
181-
writeb(opcode, chip->base);
182-
_memcpy_toio(chip->base, buf, len);
275+
aspeed_smc_to_fifo(chip->base, &opcode, 1);
276+
aspeed_smc_to_fifo(chip->base, buf, len);
183277
aspeed_smc_stop_user(nor);
184278

185279
return 0;
@@ -202,12 +296,12 @@ static void aspeed_smc_send_cmd_addr(struct spi_nor *nor, u8 cmd, u32 addr)
202296
cmdaddr |= (u32)cmd << 24;
203297

204298
temp = cpu_to_be32(cmdaddr);
205-
memcpy_toio(chip->base, &temp, 4);
299+
aspeed_smc_to_fifo(chip->base, &temp, 4);
206300
break;
207301
case 4:
208302
temp = cpu_to_be32(addr);
209-
writeb(cmd, chip->base);
210-
memcpy_toio(chip->base, &temp, 4);
303+
aspeed_smc_to_fifo(chip->base, &cmd, 1);
304+
aspeed_smc_to_fifo(chip->base, &temp, 4);
211305
break;
212306
}
213307
}
@@ -219,7 +313,7 @@ static int aspeed_smc_read_user(struct spi_nor *nor, loff_t from, size_t len,
219313

220314
aspeed_smc_start_user(nor);
221315
aspeed_smc_send_cmd_addr(nor, nor->read_opcode, from);
222-
memcpy_fromio(read_buf, chip->base, len);
316+
aspeed_smc_from_fifo(read_buf, chip->base, len);
223317
*retlen += len;
224318
aspeed_smc_stop_user(nor);
225319

@@ -233,7 +327,7 @@ static void aspeed_smc_write_user(struct spi_nor *nor, loff_t to, size_t len,
233327

234328
aspeed_smc_start_user(nor);
235329
aspeed_smc_send_cmd_addr(nor, nor->program_opcode, to);
236-
memcpy_toio(chip->base, write_buf, len);
330+
aspeed_smc_to_fifo(chip->base, write_buf, len);
237331
*retlen += len;
238332
aspeed_smc_stop_user(nor);
239333
}
@@ -292,6 +386,14 @@ static int aspeed_smc_probe(struct platform_device *dev)
292386
int err = 0;
293387
unsigned int n;
294388

389+
/*
390+
* This driver passes ioremap addresses to io port accessors.
391+
* This works on arm if the IO_SPACE_LIMIT does not truncate
392+
* the address.
393+
*/
394+
if (~(unsigned long)IO_SPACE_LIMIT)
395+
return -ENODEV;
396+
295397
match = of_match_device(aspeed_smc_matches, &dev->dev);
296398
if (!match || !match->data)
297399
return -ENODEV;

0 commit comments

Comments
 (0)