// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Microchip ENCX24J600 ethernet driver
*
* Copyright (C) 2015 Gridpoint
* Author: Jon Ringle <jringle@gridpoint.com>
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/regmap.h>
#include <linux/skbuff.h>
#include <linux/spi/spi.h>
#include "encx24j600_hw.h"
#define DRV_NAME "encx24j600"
#define DRV_VERSION "1.0"
#define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK)
static int debug = -1;
module_param(debug, int, 0000);
MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
/* SRAM memory layout:
*
* 0x0000-0x05ff TX buffers 1.5KB (1*1536) reside in the GP area in SRAM
* 0x0600-0x5fff RX buffers 22.5KB (15*1536) reside in the RX area in SRAM
*/
#define ENC_TX_BUF_START 0x0000U
#define ENC_RX_BUF_START 0x0600U
#define ENC_RX_BUF_END 0x5fffU
#define ENC_SRAM_SIZE 0x6000U
enum {
RXFILTER_NORMAL,
RXFILTER_MULTI,
RXFILTER_PROMISC
};
struct encx24j600_priv {
struct net_device *ndev;
struct mutex lock; /* device access lock */
struct encx24j600_context ctx;
struct sk_buff *tx_skb;
struct task_struct *kworker_task;
struct kthread_worker kworker;
struct kthread_work tx_work;
struct kthread_work setrx_work;
u16 next_packet;
bool hw_enabled;
bool full_duplex;
bool autoneg;
u16 speed;
int rxfilter;
u32 msg_enable;
};
static void dump_packet(const char *msg, int len, const char *data)
{
pr_debug(DRV_NAME ": %s - packet len:%d\n", msg, len);
print_hex_dump_bytes("pk data: ", DUMP_PREFIX_OFFSET, data, len);
}
static void encx24j600_dump_rsv(struct encx24j600_priv *priv, const char *msg,
struct rsv *rsv)
{
struct net_device *dev = priv->ndev;
netdev_info(dev, "RX packet Len:%d\n", rsv->len);
netdev_dbg(dev, "%s - NextPk: 0x%04x\n", msg,
rsv->next_packet);
netdev_dbg(dev, "RxOK: %d, DribbleNibble: %d\n",
RSV_GETBIT(rsv->rxstat, RSV_RXOK),
RSV_GETBIT(rsv->rxstat, RSV_DRIBBLENIBBLE));
netdev_dbg(dev, "CRCErr:%d, LenChkErr: %d, LenOutOfRange: %d\n",
RSV_GETBIT(rsv->rxstat, RSV_CRCERROR),
RSV_GETBIT(rsv->rxstat, RSV_LENCHECKERR),
RSV_GETBIT(rsv->rxstat, RSV_LENOUTOFRANGE));
netdev_dbg(dev, "Multicast: %d, Broadcast: %d, LongDropEvent: %d, CarrierEvent: %d\n",
RSV_GETBIT(rsv->rxstat, RSV_RXMULTICAST),
RSV_GETBIT(rsv->rxstat, RSV_RXBROADCAST),
RSV_GETBIT(rsv->rxstat, RSV_RXLONGEVDROPEV),
RSV_GETBIT(rsv->rxstat, RSV_CARRIEREV));
netdev_dbg(dev, "ControlFrame: %d, PauseFrame: %d, UnknownOp: %d, VLanTagFrame: %d\n",
RSV_GETBIT(rsv->rxstat, RSV_RXCONTROLFRAME),
RSV_GETBIT(rsv->rxstat, RSV_RXPAUSEFRAME),
RSV_GETBIT(rsv->rxstat, RSV_RXUNKNOWNOPCODE),
RSV_GETBIT(rsv->rxstat, RSV_RXTYPEVLAN));
}
static u16 encx24j600_read_reg(struct encx24j600_priv *priv, u8 reg)
{
struct net_device *dev = priv->ndev;
unsigned int val = 0;
int ret = regmap_read(priv->ctx.regmap, reg, &val);
if (unlikely(ret))
netif_err(priv, drv, dev, "%s: error %d reading reg %02x\n",
__func__, ret, reg);
return val;
}
static void encx24j600_write_reg(struct encx24j600_priv *priv, u8 reg, u16 val)
{
struct net_device *dev = priv->ndev;
int ret = regmap_write(priv->ctx.regmap, reg, val);
if (unlikely(ret))
netif_err(priv, drv, dev, "%s: error %d writing reg %02x=%04x\n",
__func__, ret, reg, val);
}
static void encx24j600_update_reg(struct encx24j600_priv *priv, u8 reg,
u16 mask, u16 val)
{
struct net_device *dev = priv->ndev;
int ret = regmap_update_bits(priv->ctx.regmap, reg, mask, val);
if (unlikely(ret))
netif_err(priv, drv, dev, "%s: error %d updating reg %02x=%04x~%04x\n",
__func__, ret, reg, val, mask);
}
static u16 encx24j600_read_phy(struct encx24j600_priv *priv, u8 reg)