aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@armlinux.org.uk>2016-08-27 12:33:03 -0400
committerDavid S. Miller <davem@davemloft.net>2016-08-28 23:44:55 -0400
commit2fb04fdf30192ff1e2b5834e9b7745889ea8bbcb (patch)
tree2a05f0268408c60503c31ecc41153ff1bec04290
parent7d13eca09ed5e477f6ecfd97a35058762228b5e4 (diff)
net: smc91x: fix SMC accesses
Commit b70661c70830 ("net: smc91x: use run-time configuration on all ARM machines") broke some ARM platforms through several mistakes. Firstly, the access size must correspond to the following rule: (a) at least one of 16-bit or 8-bit access size must be supported (b) 32-bit accesses are optional, and may be enabled in addition to the above. Secondly, it provides no emulation of 16-bit accesses, instead blindly making 16-bit accesses even when the platform specifies that only 8-bit is supported. Reorganise smc91x.h so we can make use of the existing 16-bit access emulation already provided - if 16-bit accesses are supported, use 16-bit accesses directly, otherwise if 8-bit accesses are supported, use the provided 16-bit access emulation. If neither, BUG(). This exactly reflects the driver behaviour prior to the commit being fixed. Since the conversion incorrectly cut down the available access sizes on several platforms, we also need to go through every platform and fix up the overly-restrictive access size: Arnd assumed that if a platform can perform 32-bit, 16-bit and 8-bit accesses, then only a 32-bit access size needed to be specified - not so, all available access sizes must be specified. This likely fixes some performance regressions in doing this: if a platform does not support 8-bit accesses, 8-bit accesses have been emulated by performing a 16-bit read-modify-write access. Tested on the Intel Assabet/Neponset platform, which supports only 8-bit accesses, which was broken by the original commit. Fixes: b70661c70830 ("net: smc91x: use run-time configuration on all ARM machines") Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> Tested-by: Robert Jarzmik <robert.jarzmik@free.fr> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--arch/arm/mach-pxa/idp.c3
-rw-r--r--arch/arm/mach-pxa/xcep.c3
-rw-r--r--arch/arm/mach-realview/core.c3
-rw-r--r--arch/arm/mach-sa1100/pleb.c2
-rw-r--r--arch/blackfin/mach-bf561/boards/cm_bf561.c3
-rw-r--r--arch/blackfin/mach-bf561/boards/ezkit.c3
-rw-r--r--drivers/net/ethernet/smsc/smc91x.c7
-rw-r--r--drivers/net/ethernet/smsc/smc91x.h65
-rw-r--r--include/linux/smc91x.h10
9 files changed, 73 insertions, 26 deletions
diff --git a/arch/arm/mach-pxa/idp.c b/arch/arm/mach-pxa/idp.c
index c410d84b243d..66070acaa888 100644
--- a/arch/arm/mach-pxa/idp.c
+++ b/arch/arm/mach-pxa/idp.c
@@ -83,7 +83,8 @@ static struct resource smc91x_resources[] = {
83}; 83};
84 84
85static struct smc91x_platdata smc91x_platdata = { 85static struct smc91x_platdata smc91x_platdata = {
86 .flags = SMC91X_USE_32BIT | SMC91X_USE_DMA | SMC91X_NOWAIT, 86 .flags = SMC91X_USE_8BIT | SMC91X_USE_16BIT | SMC91X_USE_32BIT |
87 SMC91X_USE_DMA | SMC91X_NOWAIT,
87}; 88};
88 89
89static struct platform_device smc91x_device = { 90static 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[] = {
120}; 120};
121 121
122static struct smc91x_platdata xcep_smc91x_info = { 122static struct smc91x_platdata xcep_smc91x_info = {
123 .flags = SMC91X_USE_32BIT | SMC91X_NOWAIT | SMC91X_USE_DMA, 123 .flags = SMC91X_USE_8BIT | SMC91X_USE_16BIT | SMC91X_USE_32BIT |
124 SMC91X_NOWAIT | SMC91X_USE_DMA,
124}; 125};
125 126
126static struct platform_device smc91x_device = { 127static struct platform_device smc91x_device = {
diff --git a/arch/arm/mach-realview/core.c b/arch/arm/mach-realview/core.c
index baf174542e36..a0ead0ae23d6 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 = {
93}; 93};
94 94
95static struct smc91x_platdata smc91x_platdata = { 95static struct smc91x_platdata smc91x_platdata = {
96 .flags = SMC91X_USE_32BIT | SMC91X_NOWAIT, 96 .flags = SMC91X_USE_8BIT | SMC91X_USE_16BIT | SMC91X_USE_32BIT |
97 SMC91X_NOWAIT,
97}; 98};
98 99
99static struct platform_device realview_eth_device = { 100static 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[] = {
45}; 45};
46 46
47static struct smc91x_platdata smc91x_platdata = { 47static struct smc91x_platdata smc91x_platdata = {
48 .flags = SMC91X_USE_16BIT | SMC91X_NOWAIT, 48 .flags = SMC91X_USE_16BIT | SMC91X_USE_8BIT | SMC91X_NOWAIT,
49}; 49};
50 50
51static struct platform_device smc91x_device = { 51static 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 = {
146#include <linux/smc91x.h> 146#include <linux/smc91x.h>
147 147
148static struct smc91x_platdata smc91x_info = { 148static struct smc91x_platdata smc91x_info = {
149 .flags = SMC91X_USE_32BIT | SMC91X_NOWAIT, 149 .flags = SMC91X_USE_8BIT | SMC91X_USE_16BIT | SMC91X_USE_32BIT |
150 SMC91X_NOWAIT,
150 .leda = RPC_LED_100_10, 151 .leda = RPC_LED_100_10,
151 .ledb = RPC_LED_TX_RX, 152 .ledb = RPC_LED_TX_RX,
152}; 153};
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 = {
134#include <linux/smc91x.h> 134#include <linux/smc91x.h>
135 135
136static struct smc91x_platdata smc91x_info = { 136static struct smc91x_platdata smc91x_info = {
137 .flags = SMC91X_USE_32BIT | SMC91X_NOWAIT, 137 .flags = SMC91X_USE_8BIT | SMC91X_USE_16BIT | SMC91X_USE_32BIT |
138 SMC91X_NOWAIT,
138 .leda = RPC_LED_100_10, 139 .leda = RPC_LED_100_10,
139 .ledb = RPC_LED_TX_RX, 140 .ledb = RPC_LED_TX_RX,
140}; 141};
diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c
index 726b80f45906..503a3b6dce91 100644
--- a/drivers/net/ethernet/smsc/smc91x.c
+++ b/drivers/net/ethernet/smsc/smc91x.c
@@ -2275,6 +2275,13 @@ static int smc_drv_probe(struct platform_device *pdev)
2275 if (pd) { 2275 if (pd) {
2276 memcpy(&lp->cfg, pd, sizeof(lp->cfg)); 2276 memcpy(&lp->cfg, pd, sizeof(lp->cfg));
2277 lp->io_shift = SMC91X_IO_SHIFT(lp->cfg.flags); 2277 lp->io_shift = SMC91X_IO_SHIFT(lp->cfg.flags);
2278
2279 if (!SMC_8BIT(lp) && !SMC_16BIT(lp)) {
2280 dev_err(&pdev->dev,
2281 "at least one of 8-bit or 16-bit access support is required.\n");
2282 ret = -ENXIO;
2283 goto out_free_netdev;
2284 }
2278 } 2285 }
2279 2286
2280#if IS_BUILTIN(CONFIG_OF) 2287#if IS_BUILTIN(CONFIG_OF)
diff --git a/drivers/net/ethernet/smsc/smc91x.h b/drivers/net/ethernet/smsc/smc91x.h
index 1a55c7976df0..e17671c9d1b0 100644
--- a/drivers/net/ethernet/smsc/smc91x.h
+++ b/drivers/net/ethernet/smsc/smc91x.h
@@ -37,6 +37,27 @@
37#include <linux/smc91x.h> 37#include <linux/smc91x.h>
38 38
39/* 39/*
40 * Any 16-bit access is performed with two 8-bit accesses if the hardware
41 * can't do it directly. Most registers are 16-bit so those are mandatory.
42 */
43#define SMC_outw_b(x, a, r) \
44 do { \
45 unsigned int __val16 = (x); \
46 unsigned int __reg = (r); \
47 SMC_outb(__val16, a, __reg); \
48 SMC_outb(__val16 >> 8, a, __reg + (1 << SMC_IO_SHIFT)); \
49 } while (0)
50
51#define SMC_inw_b(a, r) \
52 ({ \
53 unsigned int __val16; \
54 unsigned int __reg = r; \
55 __val16 = SMC_inb(a, __reg); \
56 __val16 |= SMC_inb(a, __reg + (1 << SMC_IO_SHIFT)) << 8; \
57 __val16; \
58 })
59
60/*
40 * Define your architecture specific bus configuration parameters here. 61 * Define your architecture specific bus configuration parameters here.
41 */ 62 */
42 63
@@ -55,10 +76,30 @@
55#define SMC_IO_SHIFT (lp->io_shift) 76#define SMC_IO_SHIFT (lp->io_shift)
56 77
57#define SMC_inb(a, r) readb((a) + (r)) 78#define SMC_inb(a, r) readb((a) + (r))
58#define SMC_inw(a, r) readw((a) + (r)) 79#define SMC_inw(a, r) \
80 ({ \
81 unsigned int __smc_r = r; \
82 SMC_16BIT(lp) ? readw((a) + __smc_r) : \
83 SMC_8BIT(lp) ? SMC_inw_b(a, __smc_r) : \
84 ({ BUG(); 0; }); \
85 })
86
59#define SMC_inl(a, r) readl((a) + (r)) 87#define SMC_inl(a, r) readl((a) + (r))
60#define SMC_outb(v, a, r) writeb(v, (a) + (r)) 88#define SMC_outb(v, a, r) writeb(v, (a) + (r))
89#define SMC_outw(v, a, r) \
90 do { \
91 unsigned int __v = v, __smc_r = r; \
92 if (SMC_16BIT(lp)) \
93 __SMC_outw(__v, a, __smc_r); \
94 else if (SMC_8BIT(lp)) \
95 SMC_outw_b(__v, a, __smc_r); \
96 else \
97 BUG(); \
98 } while (0)
99
61#define SMC_outl(v, a, r) writel(v, (a) + (r)) 100#define SMC_outl(v, a, r) writel(v, (a) + (r))
101#define SMC_insb(a, r, p, l) readsb((a) + (r), p, l)
102#define SMC_outsb(a, r, p, l) writesb((a) + (r), p, l)
62#define SMC_insw(a, r, p, l) readsw((a) + (r), p, l) 103#define SMC_insw(a, r, p, l) readsw((a) + (r), p, l)
63#define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l) 104#define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l)
64#define SMC_insl(a, r, p, l) readsl((a) + (r), p, l) 105#define SMC_insl(a, r, p, l) readsl((a) + (r), p, l)
@@ -66,7 +107,7 @@
66#define SMC_IRQ_FLAGS (-1) /* from resource */ 107#define SMC_IRQ_FLAGS (-1) /* from resource */
67 108
68/* We actually can't write halfwords properly if not word aligned */ 109/* We actually can't write halfwords properly if not word aligned */
69static inline void SMC_outw(u16 val, void __iomem *ioaddr, int reg) 110static inline void __SMC_outw(u16 val, void __iomem *ioaddr, int reg)
70{ 111{
71 if ((machine_is_mainstone() || machine_is_stargate2() || 112 if ((machine_is_mainstone() || machine_is_stargate2() ||
72 machine_is_pxa_idp()) && reg & 2) { 113 machine_is_pxa_idp()) && reg & 2) {
@@ -416,24 +457,8 @@ smc_pxa_dma_insw(void __iomem *ioaddr, struct smc_local *lp, int reg, int dma,
416 457
417#if ! SMC_CAN_USE_16BIT 458#if ! SMC_CAN_USE_16BIT
418 459
419/* 460#define SMC_outw(x, ioaddr, reg) SMC_outw_b(x, ioaddr, reg)
420 * Any 16-bit access is performed with two 8-bit accesses if the hardware 461#define SMC_inw(ioaddr, reg) SMC_inw_b(ioaddr, reg)
421 * can't do it directly. Most registers are 16-bit so those are mandatory.
422 */
423#define SMC_outw(x, ioaddr, reg) \
424 do { \
425 unsigned int __val16 = (x); \
426 SMC_outb( __val16, ioaddr, reg ); \
427 SMC_outb( __val16 >> 8, ioaddr, reg + (1 << SMC_IO_SHIFT));\
428 } while (0)
429#define SMC_inw(ioaddr, reg) \
430 ({ \
431 unsigned int __val16; \
432 __val16 = SMC_inb( ioaddr, reg ); \
433 __val16 |= SMC_inb( ioaddr, reg + (1 << SMC_IO_SHIFT)) << 8; \
434 __val16; \
435 })
436
437#define SMC_insw(a, r, p, l) BUG() 462#define SMC_insw(a, r, p, l) BUG()
438#define SMC_outsw(a, r, p, l) BUG() 463#define SMC_outsw(a, r, p, l) BUG()
439 464
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 @@
1#ifndef __SMC91X_H__ 1#ifndef __SMC91X_H__
2#define __SMC91X_H__ 2#define __SMC91X_H__
3 3
4/*
5 * These bits define which access sizes a platform can support, rather
6 * than the maximal access size. So, if your platform can do 16-bit
7 * and 32-bit accesses to the SMC91x device, but not 8-bit, set both
8 * SMC91X_USE_16BIT and SMC91X_USE_32BIT.
9 *
10 * The SMC91x driver requires at least one of SMC91X_USE_8BIT or
11 * SMC91X_USE_16BIT to be supported - just setting SMC91X_USE_32BIT is
12 * an invalid configuration.
13 */
4#define SMC91X_USE_8BIT (1 << 0) 14#define SMC91X_USE_8BIT (1 << 0)
5#define SMC91X_USE_16BIT (1 << 1) 15#define SMC91X_USE_16BIT (1 << 1)
6#define SMC91X_USE_32BIT (1 << 2) 16#define SMC91X_USE_32BIT (1 << 2)