[PATCH] smc91x: remove ARM hack for unaligned 16-bit writes
From: linux@armlinux.org.uk (Russell King - ARM Linux)
Date: 2016-08-26 12:30:39
Also in:
lkml, netdev
Subsystem:
arm port, pxa2xx/pxa3xx support, the rest · Maintainers:
Russell King, Daniel Mack, Haojian Zhuang, Robert Jarzmik, Linus Torvalds
Possibly related (same subject, not in this thread)
- 2016-08-25 · Re: [PATCH] smc91x: remove ARM hack for unaligned 16-bit writes · Arnd Bergmann <arnd@arndb.de>
- 2016-08-25 · [PATCH] smc91x: remove ARM hack for unaligned 16-bit writes · Arnd Bergmann <arnd@arndb.de>
On Fri, Aug 26, 2016 at 12:38:48PM +0100, Russell King - ARM Linux wrote:
On Fri, Aug 26, 2016 at 11:41:21AM +0200, Arnd Bergmann wrote:quoted
On Thursday, August 25, 2016 11:37:43 PM CEST Russell King - ARM Linux wrote:quoted
On Thu, Aug 25, 2016 at 08:02:35PM +0200, Robert Jarzmik wrote:quoted
Arnd Bergmann [off-list ref] writes:/* + * Any 16-bit access is performed with two 8-bit accesses if the hardware + * can't do it directly. Most registers are 16-bit so those are mandatory. + */ +#define SMC_outw_b(x, a, r) \ + do { \ + unsigned int __val16 = (x); \ + unsigned int __reg = (r); \ + SMC_outb(__val16, a, __reg); \ + SMC_outb(__val16 >> 8, a, __reg + (1 << SMC_IO_SHIFT)); \ + } while (0) + +#define SMC_inw_b(a, r) \ + ({ \ + unsigned int __val16; \ + unsigned int __reg = r; \ + __val16 = SMC_inb(a, __reg); \ + __val16 |= SMC_inb(a, __reg + (1 << SMC_IO_SHIFT)) << 8; \ + __val16; \ + }) + +/* * Define your architecture specific bus configuration parameters here. */@@ -55,10 +76,30 @@ #define SMC_IO_SHIFT (lp->io_shift) #define SMC_inb(a, r) readb((a) + (r)) -#define SMC_inw(a, r) readw((a) + (r)) +#define SMC_inw(a, r) \ + ({ \ + unsigned int __smc_r = r; \ + SMC_16BIT(lp) ? readw((a) + __smc_r) : \ + SMC_8BIT(lp) ? SMC_inw_b(a, __smc_r) : \ + ({ BUG(); 0; }); \ + }) +I think this breaks machines that declare a device that just lists SMC91X_USE_32BIT but not SMC91X_USE_16BIT. Right now, the way this is interpreted is to use 32-bit accessors for most things, but not avoiding 16-bit reads.Your comment is wrong. It breaks machines that declare a device with SMC91X_USE_32BIT but not _either_ of SMC91X_USE_16BIT _or_ SMC91X_USE_8BIT. As I already noted, but you obviously ignored. In that note, I pointed out that this _was_ already the case with the original driver before your patch: #if ! SMC_CAN_USE_16BIT ... code implementing SMC_outw() and SMC_inw() in terms of SMC_outb() and SMC_inb(), and defining SMC_insw() and SMC_outsw() as BUG() #endif #if !defined(SMC_insw) || !defined(SMC_outsw) #define SMC_insw(a, r, p, l) BUG() #define SMC_outsw(a, r, p, l) BUG() #endif #if ! SMC_CAN_USE_8BIT #define SMC_inb(ioaddr, reg) ({ BUG(); 0; }) #define SMC_outb(x, ioaddr, reg) BUG() #define SMC_insb(a, r, p, l) BUG() #define SMC_outsb(a, r, p, l) BUG() #endif #if !defined(SMC_insb) || !defined(SMC_outsb) #define SMC_insb(a, r, p, l) BUG() #define SMC_outsb(a, r, p, l) BUG() #endif So, if _neither_ SMC_CAN_USE_16BIT nor SMC_CAN_USE_8BIT are defined, trying to use the 16-bit accessors cause a BUG().quoted
That is a bit fishy though, and we could instead change the platform data to always set both SMC91X_USE_32BIT and SMC91X_USE_16BIT. The affected platforms are DT based machines with 32-bit I/O and these board files: arch/arm/mach-pxa/idp.c: .flags = SMC91X_USE_32BIT | SMC91X_USE_DMA | SMC91X_NOWAIT, arch/arm/mach-pxa/xcep.c: .flags = SMC91X_USE_32BIT | SMC91X_NOWAIT | SMC91X_USE_DMA, arch/arm/mach-realview/core.c: .flags = SMC91X_USE_32BIT | SMC91X_NOWAIT, arch/blackfin/mach-bf561/boards/cm_bf561.c: .flags = SMC91X_USE_32BIT | SMC91X_NOWAIT, arch/blackfin/mach-bf561/boards/ezkit.c: .flags = SMC91X_USE_32BIT | SMC91X_NOWAIT,Right, this looks like another one of your bugs in this conversion. #elif defined(CONFIG_ARCH_INNOKOM) || \ defined(CONFIG_ARCH_PXA_IDP) || \ defined(CONFIG_ARCH_RAMSES) || \ defined(CONFIG_ARCH_PCM027) #define SMC_CAN_USE_8BIT 1 #define SMC_CAN_USE_16BIT 1 #define SMC_CAN_USE_32BIT 1 So, IDP can use all of 32-bit, 16-bit and 8-bit accesses, meanwhile your conversion is telling the driver that it can only use 32-bit => your conversion of IDP is broken. Before your conversion, realview depended on the default settings of smc91x, which is again: #define SMC_CAN_USE_8BIT 1 #define SMC_CAN_USE_16BIT 1 #define SMC_CAN_USE_32BIT 1 So realview follows IDP - it can do 32-bit, 16-bit and 8-bit accesses, meanwhile your conversion tells the driver it can _only_ do 32-bit accesses, which is again broken. XCEP falls into the same category, as do the other two blackfin cases from what I can see. The point of the SMC_CAN_USE_xxx macros is to indicate to the SMC91x driver which sizes of access can be used on the hardware concerned. If SMC_CAN_USE_16BIT is set, then 16-bit accesses are possible. If it's not set, then 8-bit accesses will be used if they're supported, otherwise it's a buggy configuration. SMC_CAN_USE_32BIT says that 32-bit accesses are possible, but these are only used in a small number of cases as an optimisation. It is a bug to configure the smc91x driver with both SMC_CAN_USE_16BIT and SMC_CAN_USE_8BIT clear. Hence, it's a bug to tell the driver (via the platform data) that only 32-bit accesses can be performed. So, while my patch may be fixing some of the brokenness, the rest of the brokenness also needs fixing by adjusting all the platform data to properly reflect which access sizes are possible, rather than what you seem to have done, which is to indicate what the largest possible access size is. This is especially important as there are platforms around where 16-bit accesses to the SMC91x can be performed, but not 8-bit: #elif defined(CONFIG_MACH_LOGICPD_PXA270) || \ defined(CONFIG_MACH_NOMADIK_8815NHK) #define SMC_CAN_USE_8BIT 0 #define SMC_CAN_USE_16BIT 1 #define SMC_CAN_USE_32BIT 0 #elif defined(CONFIG_SH_SH4202_MICRODEV) #define SMC_CAN_USE_8BIT 0 #define SMC_CAN_USE_16BIT 1 #define SMC_CAN_USE_32BIT 0 #elif defined(CONFIG_M32R) #define SMC_CAN_USE_8BIT 0 #define SMC_CAN_USE_16BIT 1 #define SMC_CAN_USE_32BIT 0 #elif defined(CONFIG_ARCH_MSM) #define SMC_CAN_USE_8BIT 0 #define SMC_CAN_USE_16BIT 1 #define SMC_CAN_USE_32BIT 0 #elif defined(CONFIG_COLDFIRE) #define SMC_CAN_USE_8BIT 0 #define SMC_CAN_USE_16BIT 1 #define SMC_CAN_USE_32BIT 0 ... etc ...
So, based on your patch introducing the breakage, these changes are necessary to restore the per-platform capabilities that were originally configured into the driver. I've also checked over the tree, and added the others that only set SMC91X_USE_32BIT. Lastly, I've added a comment to linux/smc91x.h indicating how these bits are supposed to be used. There may be other platforms which need updating - setting these bits incorrectly can lead to more complex IO accesses than are necessary, which, in a SMP environment, would be racy. For instance, a missing SMC91X_USE_8BIT results in the interrupt acknowledgement being written using a read-modify-write sequence with a 16-bit access under local_irq_save(). That's also another illustration why supporting at least one of 8-bit or 16-bit access is mandatory - there is no emulation of the 8 or 16-bit accesses in terms of 32-bit accessors. arch/arm/mach-pxa/idp.c | 3 ++- arch/arm/mach-pxa/xcep.c | 3 ++- arch/arm/mach-realview/core.c | 3 ++- arch/arm/mach-sa1100/pleb.c | 2 +- arch/blackfin/mach-bf561/boards/cm_bf561.c | 3 ++- arch/blackfin/mach-bf561/boards/ezkit.c | 3 ++- include/linux/smc91x.h | 10 ++++++++++ 7 files changed, 21 insertions(+), 6 deletions(-)
diff --git a/arch/arm/mach-pxa/idp.c b/arch/arm/mach-pxa/idp.c
index c410d84b243d..30100ac94368 100644
--- a/arch/arm/mach-pxa/idp.c
+++ b/arch/arm/mach-pxa/idp.c@@ -83,7 +83,8 @@ static struct resource smc91x_resources[] = { }; static struct smc91x_platdata smc91x_platdata = { - .flags = SMC91X_USE_32BIT | SMC91X_USE_DMA | SMC91X_NOWAIT, + .flags = SMC91X_USE_8BIT | SMC91X_USE_16BIT | SMC91X_USE_32BIT | + SMC91X_USE_DMA | SMC91X_NOWAIT, }; static struct platform_device smc91x_device = {
diff --git a/arch/arm/mach-pxa/xcep.c b/arch/arm/mach-pxa/xcep.c
index 3f06cd90567a..056369ef250e 100644
--- a/arch/arm/mach-pxa/xcep.c
+++ b/arch/arm/mach-pxa/xcep.c@@ -120,7 +120,8 @@ static struct resource smc91x_resources[] = { }; static struct smc91x_platdata xcep_smc91x_info = { - .flags = SMC91X_USE_32BIT | SMC91X_NOWAIT | SMC91X_USE_DMA, + .flags = SMC91X_USE_8BIT | SMC91X_USE_16BIT | SMC91X_USE_32BIT | + SMC91X_NOWAIT | SMC91X_USE_DMA, }; static struct platform_device smc91x_device = {
diff --git a/arch/arm/mach-realview/core.c b/arch/arm/mach-realview/core.c
index baf174542e36..b287deaf582c 100644
--- a/arch/arm/mach-realview/core.c
+++ b/arch/arm/mach-realview/core.c@@ -93,7 +93,8 @@ static struct smsc911x_platform_config smsc911x_config = { }; static struct smc91x_platdata smc91x_platdata = { - .flags = SMC91X_USE_32BIT | SMC91X_NOWAIT, + .flags = SMC91X_USE_8BIT | SMC91X_USE_16BIT | SMC91X_USE_32BIT | + SMC91X_NOWAIT, }; static struct platform_device realview_eth_device = {
diff --git a/arch/arm/mach-sa1100/pleb.c b/arch/arm/mach-sa1100/pleb.c
index 1525d7b5f1b7..88149f85bc49 100644
--- a/arch/arm/mach-sa1100/pleb.c
+++ b/arch/arm/mach-sa1100/pleb.c@@ -45,7 +45,7 @@ static struct resource smc91x_resources[] = { }; static struct smc91x_platdata smc91x_platdata = { - .flags = SMC91X_USE_16BIT | SMC91X_NOWAIT, + .flags = SMC91X_USE_16BIT | SMC91X_USE_8BIT | SMC91X_NOWAIT, }; static struct platform_device smc91x_device = {
diff --git a/arch/blackfin/mach-bf561/boards/cm_bf561.c b/arch/blackfin/mach-bf561/boards/cm_bf561.c
index c6db52ba3a06..10c57771822d 100644
--- a/arch/blackfin/mach-bf561/boards/cm_bf561.c
+++ b/arch/blackfin/mach-bf561/boards/cm_bf561.c@@ -146,7 +146,8 @@ static struct platform_device hitachi_fb_device = { #include <linux/smc91x.h> static struct smc91x_platdata smc91x_info = { - .flags = SMC91X_USE_32BIT | SMC91X_NOWAIT, + .flags = SMC91X_USE_8BIT | SMC91X_USE_16BIT | SMC91X_USE_32BIT | + SMC91X_NOWAIT, .leda = RPC_LED_100_10, .ledb = RPC_LED_TX_RX, };
diff --git a/arch/blackfin/mach-bf561/boards/ezkit.c b/arch/blackfin/mach-bf561/boards/ezkit.c
index f35525b55819..57d1c43726d9 100644
--- a/arch/blackfin/mach-bf561/boards/ezkit.c
+++ b/arch/blackfin/mach-bf561/boards/ezkit.c@@ -134,7 +134,8 @@ static struct platform_device net2272_bfin_device = { #include <linux/smc91x.h> static struct smc91x_platdata smc91x_info = { - .flags = SMC91X_USE_32BIT | SMC91X_NOWAIT, + .flags = SMC91X_USE_8BIT | SMC91X_USE_16BIT | SMC91X_USE_32BIT | + SMC91X_NOWAIT, .leda = RPC_LED_100_10, .ledb = RPC_LED_TX_RX, };
diff --git a/include/linux/smc91x.h b/include/linux/smc91x.h
index 76199b75d584..e302c447e057 100644
--- a/include/linux/smc91x.h
+++ b/include/linux/smc91x.h@@ -1,6 +1,16 @@ #ifndef __SMC91X_H__ #define __SMC91X_H__ +/* + * These bits define which access sizes a platform can support, rather + * than the maximal access size. So, if your platform can do 16-bit + * and 32-bit accesses to the SMC91x device, but not 8-bit, set both + * SMC91X_USE_16BIT and SMC91X_USE_32BIT. + * + * The SMC91x driver requires at least one of SMC91X_USE_8BIT or + * SMC91X_USE_16BIT to be supported - just setting SMC91X_USE_32BIT is + * an invalid configuration. + */ #define SMC91X_USE_8BIT (1 << 0) #define SMC91X_USE_16BIT (1 << 1) #define SMC91X_USE_32BIT (1 << 2)
--
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently@9.6Mbps down 400kbps up
according to speedtest.net.