Commit e2359fad authored by David S. Miller's avatar David S. Miller

Merge branch 'defxx-updates'

Maciej W. Rozycki says:

====================
FDDI: defxx: CSR access fixes and improvements

As a lab upgrade I have recently replaced a dated 32-bit x86 server with
a new POWER9 system.  One of the purposes of the system has been providing
network based resources to clients over my FDDI network.  As such the new
server has also received a new DEFPA FDDI network adapter.

 As it turned out the interface did not work with the driver as shipped by
the most recent stable Debian release (Linux version 5.9.15) for ppc64el.
Symptoms were inconclusive, and the DEFPA adapter turned out to have a
manufacturing defect as well, however eventually I have figured out the
PCIe host bridge used with the system, Power Systems Host Bridge 4 (PHB4),
does not (anymore) implement PCI I/O transactions, while the binary defxx
driver as shipped by Debian comes configured for port I/O, and then a bug
in resource handling causes the driver to try and use an unassigned port
I/O range for adapter's PDQ main ASIC's CSR access.

 Fortunately the PFI PCI interface ASIC used with the DEFPA adapter has
been designed such as to provide for both PCI I/O and PCI memory accesses
to be used for PDQ CSR access, via a pair of BARs to be alternatively
used.

 Originally the defxx driver only supported port I/O access, but in the
course of interfacing it to the TURBOchannel bus I had to implement MMIO
access too, and while at it I have added a kernel configuration option to
globally switch between port I/O and MMIO at compilation time, however
conservatively defaulting to port I/O for EISA bus support where the use
of MMIO currently requires the adapter to have been suitably configured
via ECU (EISA Configuration Utility), supplied externally.

 With the kernel configuration option set to MMIO the DEFPA interface
works correctly with my POWER9 system.  Therefore I have prepared this
small patch series consisting of a pair of conservative bug fixes, to be
backported to stable branches, and then a pair of improvements for the
robustness of the driver.

 So changes 1/4 and 2/4 apply both to net and net-next, and then changes
3/4 and 4/4 apply on top of them to net-next only.  In particular there
are diff context dependencies going like this: 1/4 -> 3/4 -> 4/4.  Let me
know if this submission needs to be sorted differently.

 See individual change descriptions for further details as to the actual
changes made.

 NB the ESIC interface chip used for slave address decoding with the DEFEA
EISA adapter has decoding implemented for address bits 31:10 and therefore
supports full 32-bit range for the allocation of the CSR decoding window.
For DOS compatibility reasons ECU however only allows allocations between
0x000c0000 and 0x000effff.

 Given that for other compatibility reasons EISA is subtractively decoded
on mixed PCI/EISA systems we could allocate an MMIO region from arbitrary
unoccupied memory space and program the ESIC suitably without regard for
that compatibility limitation.  In fact I have a proof-of-concept change
and it seems to work reliably.

 However with these patches applied the driver continues supporting port
I/O as fallback and the EISA product ID register is located in the EISA
slot-specific port I/O address space, so any EISA system however modern
(sounds like a joke, eh?) also has to support port I/O access somehow.

 So while I think such a dynamic MMIO allocation would be an example of
good engineering, but it would require changes to our EISA core and
therefore it may have had sense 25 years ago when EISA was still
mainstream, but not nowadays when EISA systems are I suppose more of a
curiosity rather than the usual equipment.

 This patch series has been thoroughly verified with Linux 5.11.0 as
released and then a Raptor Talos II POWER9 system and a Malta 5Kc MIPS64
system for PCI DEFPA adapter support, an Advanced Integrated Research
486EI x86 system for EISA DEFEA adapter support, and a Digital Equipment
DECstation 5000 model 260 MIPS III system for TURBOchannel DEFTA adapter
support, covering both port I/O and MMIO operation where applicable.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents a3c39230 4e052626
...@@ -38,22 +38,6 @@ config DEFXX ...@@ -38,22 +38,6 @@ config DEFXX
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called defxx. If unsure, say N. will be called defxx. If unsure, say N.
config DEFXX_MMIO
bool
prompt "Use MMIO instead of PIO" if PCI || EISA
depends on DEFXX
default n if PCI || EISA
default y
help
This instructs the driver to use EISA or PCI memory-mapped I/O
(MMIO) as appropriate instead of programmed I/O ports (PIO).
Enabling this gives an improvement in processing time in parts
of the driver, but it may cause problems with EISA (DEFEA)
adapters. TURBOchannel does not have the concept of I/O ports,
so MMIO is always used for these (DEFTA) adapters.
If unsure, say N.
config SKFP config SKFP
tristate "SysKonnect FDDI PCI support" tristate "SysKonnect FDDI PCI support"
depends on FDDI && PCI depends on FDDI && PCI
......
...@@ -197,6 +197,7 @@ ...@@ -197,6 +197,7 @@
* 23 Oct 2006 macro Big-endian host support. * 23 Oct 2006 macro Big-endian host support.
* 14 Dec 2006 macro TURBOchannel support. * 14 Dec 2006 macro TURBOchannel support.
* 01 Jul 2014 macro Fixes for DMA on 64-bit hosts. * 01 Jul 2014 macro Fixes for DMA on 64-bit hosts.
* 10 Mar 2021 macro Dynamic MMIO vs port I/O.
*/ */
/* Include files */ /* Include files */
...@@ -225,8 +226,8 @@ ...@@ -225,8 +226,8 @@
/* Version information string should be updated prior to each new release! */ /* Version information string should be updated prior to each new release! */
#define DRV_NAME "defxx" #define DRV_NAME "defxx"
#define DRV_VERSION "v1.11" #define DRV_VERSION "v1.12"
#define DRV_RELDATE "2014/07/01" #define DRV_RELDATE "2021/03/10"
static const char version[] = static const char version[] =
DRV_NAME ": " DRV_VERSION " " DRV_RELDATE DRV_NAME ": " DRV_VERSION " " DRV_RELDATE
...@@ -253,10 +254,10 @@ static const char version[] = ...@@ -253,10 +254,10 @@ static const char version[] =
#define DFX_BUS_TC(dev) 0 #define DFX_BUS_TC(dev) 0
#endif #endif
#ifdef CONFIG_DEFXX_MMIO #if defined(CONFIG_EISA) || defined(CONFIG_PCI)
#define DFX_MMIO 1 #define dfx_use_mmio bp->mmio
#else #else
#define DFX_MMIO 0 #define dfx_use_mmio true
#endif #endif
/* Define module-wide (static) routines */ /* Define module-wide (static) routines */
...@@ -374,8 +375,6 @@ static inline void dfx_outl(DFX_board_t *bp, int offset, u32 data) ...@@ -374,8 +375,6 @@ static inline void dfx_outl(DFX_board_t *bp, int offset, u32 data)
static void dfx_port_write_long(DFX_board_t *bp, int offset, u32 data) static void dfx_port_write_long(DFX_board_t *bp, int offset, u32 data)
{ {
struct device __maybe_unused *bdev = bp->bus_dev; struct device __maybe_unused *bdev = bp->bus_dev;
int dfx_bus_tc = DFX_BUS_TC(bdev);
int dfx_use_mmio = DFX_MMIO || dfx_bus_tc;
if (dfx_use_mmio) if (dfx_use_mmio)
dfx_writel(bp, offset, data); dfx_writel(bp, offset, data);
...@@ -398,8 +397,6 @@ static inline void dfx_inl(DFX_board_t *bp, int offset, u32 *data) ...@@ -398,8 +397,6 @@ static inline void dfx_inl(DFX_board_t *bp, int offset, u32 *data)
static void dfx_port_read_long(DFX_board_t *bp, int offset, u32 *data) static void dfx_port_read_long(DFX_board_t *bp, int offset, u32 *data)
{ {
struct device __maybe_unused *bdev = bp->bus_dev; struct device __maybe_unused *bdev = bp->bus_dev;
int dfx_bus_tc = DFX_BUS_TC(bdev);
int dfx_use_mmio = DFX_MMIO || dfx_bus_tc;
if (dfx_use_mmio) if (dfx_use_mmio)
dfx_readl(bp, offset, data); dfx_readl(bp, offset, data);
...@@ -421,7 +418,7 @@ static void dfx_port_read_long(DFX_board_t *bp, int offset, u32 *data) ...@@ -421,7 +418,7 @@ static void dfx_port_read_long(DFX_board_t *bp, int offset, u32 *data)
* None * None
* *
* Arguments: * Arguments:
* bdev - pointer to device information * bp - pointer to board information
* bar_start - pointer to store the start addresses * bar_start - pointer to store the start addresses
* bar_len - pointer to store the lengths of the areas * bar_len - pointer to store the lengths of the areas
* *
...@@ -431,13 +428,13 @@ static void dfx_port_read_long(DFX_board_t *bp, int offset, u32 *data) ...@@ -431,13 +428,13 @@ static void dfx_port_read_long(DFX_board_t *bp, int offset, u32 *data)
* Side Effects: * Side Effects:
* None * None
*/ */
static void dfx_get_bars(struct device *bdev, static void dfx_get_bars(DFX_board_t *bp,
resource_size_t *bar_start, resource_size_t *bar_len) resource_size_t *bar_start, resource_size_t *bar_len)
{ {
struct device *bdev = bp->bus_dev;
int dfx_bus_pci = dev_is_pci(bdev); int dfx_bus_pci = dev_is_pci(bdev);
int dfx_bus_eisa = DFX_BUS_EISA(bdev); int dfx_bus_eisa = DFX_BUS_EISA(bdev);
int dfx_bus_tc = DFX_BUS_TC(bdev); int dfx_bus_tc = DFX_BUS_TC(bdev);
int dfx_use_mmio = DFX_MMIO || dfx_bus_tc;
if (dfx_bus_pci) { if (dfx_bus_pci) {
int num = dfx_use_mmio ? 0 : 1; int num = dfx_use_mmio ? 0 : 1;
...@@ -495,6 +492,13 @@ static const struct net_device_ops dfx_netdev_ops = { ...@@ -495,6 +492,13 @@ static const struct net_device_ops dfx_netdev_ops = {
.ndo_set_mac_address = dfx_ctl_set_mac_address, .ndo_set_mac_address = dfx_ctl_set_mac_address,
}; };
static void dfx_register_res_err(const char *print_name, bool mmio,
unsigned long start, unsigned long len)
{
pr_err("%s: Cannot reserve %s resource 0x%lx @ 0x%lx, aborting\n",
print_name, mmio ? "MMIO" : "I/O", len, start);
}
/* /*
* ================ * ================
* = dfx_register = * = dfx_register =
...@@ -528,8 +532,6 @@ static int dfx_register(struct device *bdev) ...@@ -528,8 +532,6 @@ static int dfx_register(struct device *bdev)
static int version_disp; static int version_disp;
int dfx_bus_pci = dev_is_pci(bdev); int dfx_bus_pci = dev_is_pci(bdev);
int dfx_bus_eisa = DFX_BUS_EISA(bdev); int dfx_bus_eisa = DFX_BUS_EISA(bdev);
int dfx_bus_tc = DFX_BUS_TC(bdev);
int dfx_use_mmio = DFX_MMIO || dfx_bus_tc;
const char *print_name = dev_name(bdev); const char *print_name = dev_name(bdev);
struct net_device *dev; struct net_device *dev;
DFX_board_t *bp; /* board pointer */ DFX_board_t *bp; /* board pointer */
...@@ -567,46 +569,48 @@ static int dfx_register(struct device *bdev) ...@@ -567,46 +569,48 @@ static int dfx_register(struct device *bdev)
bp->bus_dev = bdev; bp->bus_dev = bdev;
dev_set_drvdata(bdev, dev); dev_set_drvdata(bdev, dev);
dfx_get_bars(bdev, bar_start, bar_len); bp->mmio = true;
if (dfx_bus_eisa && dfx_use_mmio && bar_start[0] == 0) {
pr_err("%s: Cannot use MMIO, no address set, aborting\n", dfx_get_bars(bp, bar_start, bar_len);
print_name); if (bar_len[0] == 0 ||
pr_err("%s: Run ECU and set adapter's MMIO location\n", (dfx_bus_eisa && dfx_use_mmio && bar_start[0] == 0)) {
print_name); bp->mmio = false;
pr_err("%s: Or recompile driver with \"CONFIG_DEFXX_MMIO=n\"" dfx_get_bars(bp, bar_start, bar_len);
"\n", print_name);
err = -ENXIO;
goto err_out;
} }
if (dfx_use_mmio) if (dfx_use_mmio) {
region = request_mem_region(bar_start[0], bar_len[0], region = request_mem_region(bar_start[0], bar_len[0],
print_name); bdev->driver->name);
else if (!region && (dfx_bus_eisa || dfx_bus_pci)) {
region = request_region(bar_start[0], bar_len[0], print_name); bp->mmio = false;
dfx_get_bars(bp, bar_start, bar_len);
}
}
if (!dfx_use_mmio)
region = request_region(bar_start[0], bar_len[0],
bdev->driver->name);
if (!region) { if (!region) {
pr_err("%s: Cannot reserve %s resource 0x%lx @ 0x%lx, " dfx_register_res_err(print_name, dfx_use_mmio,
"aborting\n", dfx_use_mmio ? "MMIO" : "I/O", print_name, bar_start[0], bar_len[0]);
(long)bar_len[0], (long)bar_start[0]);
err = -EBUSY; err = -EBUSY;
goto err_out_disable; goto err_out_disable;
} }
if (bar_start[1] != 0) { if (bar_start[1] != 0) {
region = request_region(bar_start[1], bar_len[1], print_name); region = request_region(bar_start[1], bar_len[1],
bdev->driver->name);
if (!region) { if (!region) {
pr_err("%s: Cannot reserve I/O resource " dfx_register_res_err(print_name, 0,
"0x%lx @ 0x%lx, aborting\n", print_name, bar_start[1], bar_len[1]);
(long)bar_len[1], (long)bar_start[1]);
err = -EBUSY; err = -EBUSY;
goto err_out_csr_region; goto err_out_csr_region;
} }
} }
if (bar_start[2] != 0) { if (bar_start[2] != 0) {
region = request_region(bar_start[2], bar_len[2], print_name); region = request_region(bar_start[2], bar_len[2],
bdev->driver->name);
if (!region) { if (!region) {
pr_err("%s: Cannot reserve I/O resource " dfx_register_res_err(print_name, 0,
"0x%lx @ 0x%lx, aborting\n", print_name, bar_start[2], bar_len[2]);
(long)bar_len[2], (long)bar_start[2]);
err = -EBUSY; err = -EBUSY;
goto err_out_bh_region; goto err_out_bh_region;
} }
...@@ -721,7 +725,6 @@ static void dfx_bus_init(struct net_device *dev) ...@@ -721,7 +725,6 @@ static void dfx_bus_init(struct net_device *dev)
int dfx_bus_pci = dev_is_pci(bdev); int dfx_bus_pci = dev_is_pci(bdev);
int dfx_bus_eisa = DFX_BUS_EISA(bdev); int dfx_bus_eisa = DFX_BUS_EISA(bdev);
int dfx_bus_tc = DFX_BUS_TC(bdev); int dfx_bus_tc = DFX_BUS_TC(bdev);
int dfx_use_mmio = DFX_MMIO || dfx_bus_tc;
u8 val; u8 val;
DBG_printk("In dfx_bus_init...\n"); DBG_printk("In dfx_bus_init...\n");
...@@ -1041,7 +1044,6 @@ static int dfx_driver_init(struct net_device *dev, const char *print_name, ...@@ -1041,7 +1044,6 @@ static int dfx_driver_init(struct net_device *dev, const char *print_name,
int dfx_bus_pci = dev_is_pci(bdev); int dfx_bus_pci = dev_is_pci(bdev);
int dfx_bus_eisa = DFX_BUS_EISA(bdev); int dfx_bus_eisa = DFX_BUS_EISA(bdev);
int dfx_bus_tc = DFX_BUS_TC(bdev); int dfx_bus_tc = DFX_BUS_TC(bdev);
int dfx_use_mmio = DFX_MMIO || dfx_bus_tc;
int alloc_size; /* total buffer size needed */ int alloc_size; /* total buffer size needed */
char *top_v, *curr_v; /* virtual addrs into memory block */ char *top_v, *curr_v; /* virtual addrs into memory block */
dma_addr_t top_p, curr_p; /* physical addrs into memory block */ dma_addr_t top_p, curr_p; /* physical addrs into memory block */
...@@ -3695,8 +3697,6 @@ static void dfx_unregister(struct device *bdev) ...@@ -3695,8 +3697,6 @@ static void dfx_unregister(struct device *bdev)
struct net_device *dev = dev_get_drvdata(bdev); struct net_device *dev = dev_get_drvdata(bdev);
DFX_board_t *bp = netdev_priv(dev); DFX_board_t *bp = netdev_priv(dev);
int dfx_bus_pci = dev_is_pci(bdev); int dfx_bus_pci = dev_is_pci(bdev);
int dfx_bus_tc = DFX_BUS_TC(bdev);
int dfx_use_mmio = DFX_MMIO || dfx_bus_tc;
resource_size_t bar_start[3] = {0}; /* pointers to ports */ resource_size_t bar_start[3] = {0}; /* pointers to ports */
resource_size_t bar_len[3] = {0}; /* resource lengths */ resource_size_t bar_len[3] = {0}; /* resource lengths */
int alloc_size; /* total buffer size used */ int alloc_size; /* total buffer size used */
...@@ -3716,7 +3716,7 @@ static void dfx_unregister(struct device *bdev) ...@@ -3716,7 +3716,7 @@ static void dfx_unregister(struct device *bdev)
dfx_bus_uninit(dev); dfx_bus_uninit(dev);
dfx_get_bars(bdev, bar_start, bar_len); dfx_get_bars(bp, bar_start, bar_len);
if (bar_start[2] != 0) if (bar_start[2] != 0)
release_region(bar_start[2], bar_len[2]); release_region(bar_start[2], bar_len[2]);
if (bar_start[1] != 0) if (bar_start[1] != 0)
...@@ -3748,7 +3748,7 @@ static const struct pci_device_id dfx_pci_table[] = { ...@@ -3748,7 +3748,7 @@ static const struct pci_device_id dfx_pci_table[] = {
MODULE_DEVICE_TABLE(pci, dfx_pci_table); MODULE_DEVICE_TABLE(pci, dfx_pci_table);
static struct pci_driver dfx_pci_driver = { static struct pci_driver dfx_pci_driver = {
.name = "defxx", .name = DRV_NAME,
.id_table = dfx_pci_table, .id_table = dfx_pci_table,
.probe = dfx_pci_register, .probe = dfx_pci_register,
.remove = dfx_pci_unregister, .remove = dfx_pci_unregister,
...@@ -3779,7 +3779,7 @@ MODULE_DEVICE_TABLE(eisa, dfx_eisa_table); ...@@ -3779,7 +3779,7 @@ MODULE_DEVICE_TABLE(eisa, dfx_eisa_table);
static struct eisa_driver dfx_eisa_driver = { static struct eisa_driver dfx_eisa_driver = {
.id_table = dfx_eisa_table, .id_table = dfx_eisa_table,
.driver = { .driver = {
.name = "defxx", .name = DRV_NAME,
.bus = &eisa_bus_type, .bus = &eisa_bus_type,
.probe = dfx_dev_register, .probe = dfx_dev_register,
.remove = dfx_dev_unregister, .remove = dfx_dev_unregister,
...@@ -3800,7 +3800,7 @@ MODULE_DEVICE_TABLE(tc, dfx_tc_table); ...@@ -3800,7 +3800,7 @@ MODULE_DEVICE_TABLE(tc, dfx_tc_table);
static struct tc_driver dfx_tc_driver = { static struct tc_driver dfx_tc_driver = {
.id_table = dfx_tc_table, .id_table = dfx_tc_table,
.driver = { .driver = {
.name = "defxx", .name = DRV_NAME,
.bus = &tc_bus_type, .bus = &tc_bus_type,
.probe = dfx_dev_register, .probe = dfx_dev_register,
.remove = dfx_dev_unregister, .remove = dfx_dev_unregister,
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
* 04 Aug 2003 macro Converted to the DMA API. * 04 Aug 2003 macro Converted to the DMA API.
* 23 Oct 2006 macro Big-endian host support. * 23 Oct 2006 macro Big-endian host support.
* 14 Dec 2006 macro TURBOchannel support. * 14 Dec 2006 macro TURBOchannel support.
* 10 Mar 2021 macro Dynamic MMIO vs port I/O.
*/ */
#ifndef _DEFXX_H_ #ifndef _DEFXX_H_
...@@ -1776,6 +1777,8 @@ typedef struct DFX_board_tag ...@@ -1776,6 +1777,8 @@ typedef struct DFX_board_tag
int port; int port;
} base; /* base address */ } base; /* base address */
struct device *bus_dev; struct device *bus_dev;
/* Whether to use MMIO or port I/O. */
bool mmio;
u32 full_duplex_enb; /* FDDI Full Duplex enable (1 == on, 2 == off) */ u32 full_duplex_enb; /* FDDI Full Duplex enable (1 == on, 2 == off) */
u32 req_ttrt; /* requested TTRT value (in 80ns units) */ u32 req_ttrt; /* requested TTRT value (in 80ns units) */
u32 burst_size; /* adapter burst size (enumerated) */ u32 burst_size; /* adapter burst size (enumerated) */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment