// SPDX-License-Identifier: GPL-2.0+
/* FDDI network adapter driver for DEC FDDIcontroller 700/700-C devices.
*
* Copyright (c) 2018 Maciej W. Rozycki
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* References:
*
* Dave Sawyer & Phil Weeks & Frank Itkowsky,
* "DEC FDDIcontroller 700 Port Specification",
* Revision 1.1, Digital Equipment Corporation
*/
/* ------------------------------------------------------------------------- */
/* FZA configurable parameters. */
/* The number of transmit ring descriptors; either 0 for 512 or 1 for 1024. */
#define FZA_RING_TX_MODE 0
/* The number of receive ring descriptors; from 2 up to 256. */
#define FZA_RING_RX_SIZE 256
/* End of FZA configurable parameters. No need to change anything below. */
/* ------------------------------------------------------------------------- */
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/fddidevice.h>
#include <linux/sched.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/stat.h>
#include <linux/tc.h>
#include <linux/timer.h>
#include <linux/types.h>
#include <linux/wait.h>
#include <asm/barrier.h>
#include "defza.h"
#define DRV_NAME "defza"
#define DRV_VERSION "v.1.1.4"
#define DRV_RELDATE "Oct 6 2018"
static const char version[] =
DRV_NAME ": " DRV_VERSION " " DRV_RELDATE " Maciej W. Rozycki\n";
MODULE_AUTHOR("Maciej W. Rozycki <macro@linux-mips.org>");
MODULE_DESCRIPTION("DEC FDDIcontroller 700 (DEFZA-xx) driver");
MODULE_LICENSE("GPL");
static int loopback;
module_param(loopback, int, 0644);
/* Ring Purger Multicast */
static u8 hw_addr_purger[8] = { 0x09, 0x00, 0x2b, 0x02, 0x01, 0x05 };
/* Directed Beacon Multicast */
static u8 hw_addr_beacon[8] = { 0x01, 0x80, 0xc2, 0x00, 0x01, 0x00 };
/* Shorthands for MMIO accesses that we require to be strongly ordered
* WRT preceding MMIO accesses.
*/
#define readw_o readw_relaxed
#define readl_o readl_relaxed
#define writew_o writew_relaxed
#define writel_o writel_relaxed
/* Shorthands for MMIO accesses that we are happy with being weakly ordered
* WRT preceding MMIO accesses.
*/
#define readw_u readw_relaxed
#define readl_u readl_relaxed
#define readq_u readq_relaxed
#define writew_u writew_relaxed
#define writel_u writel_relaxed
#define writeq_u writeq_relaxed
static inline struct sk_buff *fza_alloc_skb_irq(struct net_device *dev,
unsigned int length)
{
return __netdev_alloc_skb(dev, length, GFP_ATOMIC);
}
static inline struct sk_buff *fza_alloc_skb(struct net_device *dev,
unsigned int length)
{
return __netdev_alloc_skb(dev, length, GFP_KERNEL);
}
static inline void fza_skb_align(struct sk_buff *skb, unsigned int v)
{
unsigned long x, y;
x = (unsigned long)skb->data;
y = ALIGN(x, v);
skb_reserve(skb, y - x);
}
static inline void fza_reads(const void __iomem *from, void *to,
unsigned long size)
{
if (sizeof(unsigned long) == 8) {
const u64 __iomem *src = from;
const u32 __iomem *src_trail;
u64 *dst = to;
u32 *dst_trail;
for (size = (size + 3) / 4; size > 1; size -= 2)
*dst++ = readq_u(src++);
if (size) {
src_trail = (u32 __iomem *)src;
dst_trail = (u32 *)dst;
*dst_trail = readl_u(src_trail);
}
} else {
const u32 __iomem *src = from;
u32 *dst = to;
for (size = (size + 3) / 4; size; size--)
*dst++ = readl_u(src++);
}
}
static inline void fza_writes(const void *from, void __iomem *to,
unsigned long size)
{
if (sizeof(unsigned long) == 8) {
const u64 *src = from;
const u32 *src_trail;
u64 __iomem *dst = to;
u32 __iomem *dst_trail;
for (size = (size + 3) / 4; size > 1; size -= 2)
writeq_u(*src++, dst++);
if (size) {
src_trail = (u32 *)src;
dst_trail = (u32 __iomem *)dst;
writel_u(*src_trail, dst_trail);
}
} else {
const u32 *src = from;
u32 __iomem *dst = to;
for (size = (size + 3) / 4; size; size--)
writel_u(*src++, dst++);
}
}
static inline void fza_moves(const void __iomem *from, void __iomem *to,
unsigned long size)
{
if (sizeof(unsigned long) == 8) {
const u64 __iomem *src = from;
const u32 __iomem *src_trail;
u64 __iomem *dst = to;
u32 __iomem *dst_trail;
for (size = (size + 3) / 4; size > 1; size -= 2)
writeq_u(readq_u(src++), dst++);
if (size) {
src_trail = (u32 __iomem *)src;
dst_trail = (u32 __iomem *)dst;
writel_u(readl_u(src_trail), dst_trail);
}
} else {
const u32 __iomem *src = from;
u32 __iomem *dst = to;
for (size = (size + 3) / 4; size; size--)
writel_u(readl_u(src++), dst++);
}
}
static inline void fza_zeros(void __iomem *to, unsigned long size)
{
if (sizeof(unsigned long) == 8) {
u64 __iomem *dst = to;
u32 __iomem *dst_trail;
for (size = (size + 3) / 4; size > 1; size -= 2)
writeq_u(0, dst++);
if (size) {
dst_trail = (u32 __iomem *)dst;
writel_u(0, dst_trail);
}
} else {
u32 __iomem *dst = to;
for (size = (size + 3) / 4; size; size--)
writel_u(0, dst++);
}
}
static inline void fza_regs_dump(struct fza_private *fp)
{
pr_debug("%s: iomem registers:\n", fp->name);
pr_debug(" reset: 0x%04x\n", readw_o(&fp->regs->reset));
pr_debug(" interrupt event: 0x%04x\n", readw_u(&fp->regs->int_event));
pr_debug(" status: 0x%04x\n", readw_u(&fp->regs->status));
pr_debug(" interrupt mask: 0x%04x\n", readw_u(&fp->regs->int_mask));
pr_debug(" control A: 0x%04x\n", readw_u(&fp->regs->control_a));
pr_debug(" control
|