// SPDX-License-Identifier: GPL-2.0+
/*
* Driver for the National Semiconductor DP83640 PHYTER
*
* Copyright (C) 2010 OMICRON electronics GmbH
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/crc32.h>
#include <linux/ethtool.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/mii.h>
#include <linux/module.h>
#include <linux/net_tstamp.h>
#include <linux/netdevice.h>
#include <linux/if_vlan.h>
#include <linux/phy.h>
#include <linux/ptp_classify.h>
#include <linux/ptp_clock_kernel.h>
#include "dp83640_reg.h"
#define DP83640_PHY_ID 0x20005ce1
#define PAGESEL 0x13
#define MAX_RXTS 64
#define N_EXT_TS 6
#define N_PER_OUT 7
#define PSF_PTPVER 2
#define PSF_EVNT 0x4000
#define PSF_RX 0x2000
#define PSF_TX 0x1000
#define EXT_EVENT 1
#define CAL_EVENT 7
#define CAL_TRIGGER 1
#define DP83640_N_PINS 12
#define MII_DP83640_MICR 0x11
#define MII_DP83640_MISR 0x12
#define MII_DP83640_MICR_OE 0x1
#define MII_DP83640_MICR_IE 0x2
#define MII_DP83640_MISR_RHF_INT_EN 0x01
#define MII_DP83640_MISR_FHF_INT_EN 0x02
#define MII_DP83640_MISR_ANC_INT_EN 0x04
#define MII_DP83640_MISR_DUP_INT_EN 0x08
#define MII_DP83640_MISR_SPD_INT_EN 0x10
#define MII_DP83640_MISR_LINK_INT_EN 0x20
#define MII_DP83640_MISR_ED_INT_EN 0x40
#define MII_DP83640_MISR_LQ_INT_EN 0x80
#define MII_DP83640_MISR_ANC_INT 0x400
#define MII_DP83640_MISR_DUP_INT 0x800
#define MII_DP83640_MISR_SPD_INT 0x1000
#define MII_DP83640_MISR_LINK_INT 0x2000
#define MII_DP83640_MISR_INT_MASK (MII_DP83640_MISR_ANC_INT |\
MII_DP83640_MISR_DUP_INT |\
MII_DP83640_MISR_SPD_INT |\
MII_DP83640_MISR_LINK_INT)
/* phyter seems to miss the mark by 16 ns */
#define ADJTIME_FIX 16
#define SKB_TIMESTAMP_TIMEOUT 2 /* jiffies */
#if defined(__BIG_ENDIAN)
#define ENDIAN_FLAG 0
#elif defined(__LITTLE_ENDIAN)
#define ENDIAN_FLAG PSF_ENDIAN
#endif
struct dp83640_skb_info {
int ptp_type;
unsigned long tmo;
};
struct phy_rxts {
u16 ns_lo; /* ns[15:0] */
u16 ns_hi; /* overflow[1:0], ns[29:16] */
u16 sec_lo; /* sec[15:0] */
u16 sec_hi; /* sec[31:16] */
u16 seqid; /* sequenceId[15:0] */
u16 msgtype; /* messageType[3:0], hash[11:0] */
};
struct phy_txts {
u16 ns_lo; /* ns[15:0] */
u16 ns_hi; /* overflow[1:0], ns[29:16] */
u16 sec_lo; /* sec[15:0] */
u16 sec_hi; /* sec[31:16] */
};
struct rxts {
struct list_head list;
unsigned long tmo;
u64 ns;
u16 seqid;
u8 msgtype;
u16 hash;
};
struct dp83640_clock;
struct dp83640_private {
struct list_head list;
struct dp83640_clock *clock;
struct phy_device *phydev;
struct mii_timestamper mii_ts;
struct delayed_work ts_work;
int hwts_tx_en;
int hwts_rx_en;
int layer;
int version;
/* remember state of cfg0 during calibration */
int cfg0;
/* remember the last event time stamp */
struct phy_txts edata;
/* list of rx timestamps */
struct list_head rxts;
struct list_head rxpool;
struct rxts rx_pool_data[MAX_RXTS];
/* protects above three fields from concurrent access */
spinlock_t rx_lock;
/* queues of incoming and outgoing packets */
struct sk_buff_head rx_queue;
struct sk_buff_head tx_queue;
};
struct dp83640_clock {
/* keeps the instance in the 'phyter_clocks' list */
struct list_head list;
/* we create one clock instance per MII bus */
struct mii_bus *bus;
/* protects extended registers from concurrent access */
struct mutex extreg_lock;
/* remembers which page was last selected */
int page;
/* our advertised capabilities */
struct ptp_clock_info caps;
/* protects the three fields below from concurrent access */
struct mutex clock_lock;
/* the one phyter from which we shall read */
struct dp83640_private *chosen;
/* list of the other attached phyters, not chosen */
struct list_head phylist;
/* reference to our PTP hardware clock */
struct ptp_clock *ptp_clock;
};
/* globals */
enum {
CALIBRATE_GPIO,
PEROUT_GPIO,
EXTTS0_GPIO,
EXTTS1_GPIO,
EXTTS2_GPIO,
EXTTS3_GPIO,
EXTTS4_GPIO,
EXTTS5_GPIO,
GPIO_TABLE_SIZE
};
static int chosen_phy = -1;
static ushort gpio_tab[GPIO_TABLE_SIZE] = {
1, 2, 3, 4, 8, 9, 10, 11
};
module_param(chosen_phy, int, 0444);
module_param_array(gpio_tab, ushort, NULL, 0444);
MODULE_PARM_DESC(chosen_phy,
"The address of the PHY to use for the ancillary clock features");
MODULE_PARM_DESC(gpio_tab,
"Which GPIO line to use for which purpose: cal,perout,extts1,...,extts6");
static void dp83640_gpio_defaults(struct ptp_pin_desc *pd)
{
int i, index;
for (i = 0; i < DP83640_N_PINS; i++) {
snprintf(pd[i].name, sizeof(pd[i].name), "GPIO%d", 1 + i);
pd[i].index = i;
}
for (i = 0; i < GPIO_TABLE_SIZE; i++) {
if (gpio_tab[i] < 1 || gpio_tab[i] > DP83640_N_PINS) {
pr_err("gpio_tab[%d]=%hu out of range", i, gpio_tab[i]);
return;
}
}
index = gpio_tab[CALIBRATE_GPIO] - 1;
pd[index].func = PTP_PF_PHYSYNC;
pd[index].chan = 0;
index = gpio_tab[PEROUT_GPIO] - 1;
pd[index].func = PTP_PF_PEROUT;
pd[index].chan = 0;
for (i = EXTTS0_GPIO; i < GPIO_TABLE_SIZE; i++) {
index = gpio_tab[i] - 1;
pd[index].func = PTP_PF_EXTTS;
pd[index].chan = i - EXTTS0_GPIO;
}
}
/* a list of clocks and a mutex to protect it */
static LIST_HEAD(phyter_clocks);
static DEFINE_MUTEX(phyter_clocks_lock);
static void rx_timestamp_work(struct work_struct *work);
/* extended register access functions */
#define BROADCAST_ADDR 31
static inline int broadcast_write(struct phy_device *phydev, u32 regnum,
u16 val)
{
return mdiobus_write(phydev->mdio.bus, BROADCAST_ADDR, regnum, val);
}
/* Caller must hold extreg_lock. */
static int ext_read(struct phy_device *phydev, int page, u32 regnum)
{
struct dp83640_private *dp83640 = phydev->priv;
int val;
if (dp83640->clock->page != page) {
broadcast_write(phydev, PAGESEL, page);
dp83640->clock->page = page;
}
val = phy_read(phydev, regnum);
return val;
}
/* Caller must hold extreg_lock. */
static void ext_write(int broadcast, struct phy_device *phydev,
int page, u32 regnum, u16 val)
{
struct dp83640_private *dp83640 = phydev->priv;
if (dp83640->clock->page != page) {
broadcast_write(phydev, PAGESEL, page);
dp83640->clock->page = page;
}
if (broadcast)
broadcast_write(phydev, regnum, val);
else
phy_write(phydev, regnum, val);
}
/* Caller must hold extreg_lock. */
static int tdr_write(int bc, struct phy_device *dev,
const struct timespec64 *ts, u16 cmd)
{
ext_write(bc, dev, PAGE4, PTP_TDR, ts->tv_nsec & 0xffff);/* ns[15:0] */
ext_write(bc, dev, PAGE4, PTP_TDR, ts->tv_nsec >> 16); /* ns[31:16] */
ext_write(bc, dev, PAGE4, PTP_TDR, ts->tv_sec & 0xffff); /* sec[15:0] */
ext_write(bc, dev, PAGE4, PTP_TDR, ts->tv_sec >> 16); /* sec[31:16]*/
ext_write(bc, dev, PAGE4, PTP_CTL, cmd);
return 0;
}
/* convert phy timestamps into driver timestamps */
static void phy2rxts(struct phy_rxts *p, struct rxts *rxts)
{
u32 sec;
sec = p->sec_lo;
|