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

Merge branch 'lan966x-ptp'

Horatiu Vultur says:

====================
net: lan966x: Add PTP Hardward Clock support

Add support for PTP Hardware Clock (PHC) for lan966x. The switch supports
both PTP 1-step and 2-step modes.

v1->v2:
- fix commit messages
- reduce the scope of the lock ptp_lock inside the function
  lan966x_ptp_hwtstamp_set
- the rx timestamping is always enabled for all packages
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 9f397dd5 966f2e1a
......@@ -38,6 +38,7 @@ properties:
- description: register based extraction
- description: frame dma based extraction
- description: analyzer interrupt
- description: ptp interrupt
interrupt-names:
minItems: 1
......@@ -45,6 +46,7 @@ properties:
- const: xtr
- const: fdma
- const: ana
- const: ptp
resets:
items:
......
......@@ -7,4 +7,5 @@ obj-$(CONFIG_LAN966X_SWITCH) += lan966x-switch.o
lan966x-switch-objs := lan966x_main.o lan966x_phylink.o lan966x_port.o \
lan966x_mac.o lan966x_ethtool.o lan966x_switchdev.o \
lan966x_vlan.o lan966x_fdb.o lan966x_mdb.o
lan966x_vlan.o lan966x_fdb.o lan966x_mdb.o \
lan966x_ptp.o
......@@ -545,6 +545,39 @@ static int lan966x_set_pauseparam(struct net_device *dev,
return phylink_ethtool_set_pauseparam(port->phylink, pause);
}
static int lan966x_get_ts_info(struct net_device *dev,
struct ethtool_ts_info *info)
{
struct lan966x_port *port = netdev_priv(dev);
struct lan966x *lan966x = port->lan966x;
struct lan966x_phc *phc;
if (!lan966x->ptp)
return ethtool_op_get_ts_info(dev, info);
phc = &lan966x->phc[LAN966X_PHC_PORT];
info->phc_index = phc->clock ? ptp_clock_index(phc->clock) : -1;
if (info->phc_index == -1) {
info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE |
SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_SOFTWARE;
return 0;
}
info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE |
SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_SOFTWARE |
SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON) |
BIT(HWTSTAMP_TX_ONESTEP_SYNC);
info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
BIT(HWTSTAMP_FILTER_ALL);
return 0;
}
const struct ethtool_ops lan966x_ethtool_ops = {
.get_link_ksettings = lan966x_get_link_ksettings,
.set_link_ksettings = lan966x_set_link_ksettings,
......@@ -556,6 +589,7 @@ const struct ethtool_ops lan966x_ethtool_ops = {
.get_eth_mac_stats = lan966x_get_eth_mac_stats,
.get_rmon_stats = lan966x_get_eth_rmon_stats,
.get_link = ethtool_op_get_link,
.get_ts_info = lan966x_get_ts_info,
};
static void lan966x_check_stats_work(struct work_struct *work)
......
......@@ -44,6 +44,7 @@ static const struct lan966x_main_io_resource lan966x_main_iomap[] = {
{ TARGET_ORG, 0, 1 }, /* 0xe2000000 */
{ TARGET_GCB, 0x4000, 1 }, /* 0xe2004000 */
{ TARGET_QS, 0x8000, 1 }, /* 0xe2008000 */
{ TARGET_PTP, 0xc000, 1 }, /* 0xe200c000 */
{ TARGET_CHIP_TOP, 0x10000, 1 }, /* 0xe2010000 */
{ TARGET_REW, 0x14000, 1 }, /* 0xe2014000 */
{ TARGET_SYS, 0x28000, 1 }, /* 0xe2028000 */
......@@ -201,7 +202,7 @@ static int lan966x_port_ifh_xmit(struct sk_buff *skb,
val = lan_rd(lan966x, QS_INJ_STATUS);
if (!(QS_INJ_STATUS_FIFO_RDY_GET(val) & BIT(grp)) ||
(QS_INJ_STATUS_WMARK_REACHED_GET(val) & BIT(grp)))
return NETDEV_TX_BUSY;
goto err;
/* Write start of frame */
lan_wr(QS_INJ_CTRL_GAP_SIZE_SET(1) |
......@@ -213,7 +214,7 @@ static int lan966x_port_ifh_xmit(struct sk_buff *skb,
/* Wait until the fifo is ready */
err = lan966x_port_inj_ready(lan966x, grp);
if (err)
return NETDEV_TX_BUSY;
goto err;
lan_wr((__force u32)ifh[i], lan966x, QS_INJ_WR(grp));
}
......@@ -225,7 +226,7 @@ static int lan966x_port_ifh_xmit(struct sk_buff *skb,
/* Wait until the fifo is ready */
err = lan966x_port_inj_ready(lan966x, grp);
if (err)
return NETDEV_TX_BUSY;
goto err;
lan_wr(((u32 *)skb->data)[i], lan966x, QS_INJ_WR(grp));
}
......@@ -235,7 +236,7 @@ static int lan966x_port_ifh_xmit(struct sk_buff *skb,
/* Wait until the fifo is ready */
err = lan966x_port_inj_ready(lan966x, grp);
if (err)
return NETDEV_TX_BUSY;
goto err;
lan_wr(0, lan966x, QS_INJ_WR(grp));
++i;
......@@ -255,8 +256,19 @@ static int lan966x_port_ifh_xmit(struct sk_buff *skb,
dev->stats.tx_packets++;
dev->stats.tx_bytes += skb->len;
if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
LAN966X_SKB_CB(skb)->rew_op == IFH_REW_OP_TWO_STEP_PTP)
return NETDEV_TX_OK;
dev_consume_skb_any(skb);
return NETDEV_TX_OK;
err:
if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
LAN966X_SKB_CB(skb)->rew_op == IFH_REW_OP_TWO_STEP_PTP)
lan966x_ptp_txtstamp_release(port, skb);
return NETDEV_TX_BUSY;
}
static void lan966x_ifh_set_bypass(void *ifh, u64 bypass)
......@@ -289,10 +301,23 @@ static void lan966x_ifh_set_vid(void *ifh, u64 vid)
IFH_POS_TCI, IFH_LEN * 4, PACK, 0);
}
static void lan966x_ifh_set_rew_op(void *ifh, u64 rew_op)
{
packing(ifh, &rew_op, IFH_POS_REW_CMD + IFH_WID_REW_CMD - 1,
IFH_POS_REW_CMD, IFH_LEN * 4, PACK, 0);
}
static void lan966x_ifh_set_timestamp(void *ifh, u64 timestamp)
{
packing(ifh, &timestamp, IFH_POS_TIMESTAMP + IFH_WID_TIMESTAMP - 1,
IFH_POS_TIMESTAMP, IFH_LEN * 4, PACK, 0);
}
static int lan966x_port_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct lan966x_port *port = netdev_priv(dev);
__be32 ifh[IFH_LEN];
int err;
memset(ifh, 0x0, sizeof(__be32) * IFH_LEN);
......@@ -302,6 +327,15 @@ static int lan966x_port_xmit(struct sk_buff *skb, struct net_device *dev)
lan966x_ifh_set_ipv(ifh, skb->priority >= 7 ? 0x7 : skb->priority);
lan966x_ifh_set_vid(ifh, skb_vlan_tag_get(skb));
if (port->lan966x->ptp && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) {
err = lan966x_ptp_txtstamp_request(port, skb);
if (err)
return err;
lan966x_ifh_set_rew_op(ifh, LAN966X_SKB_CB(skb)->rew_op);
lan966x_ifh_set_timestamp(ifh, LAN966X_SKB_CB(skb)->ts_id);
}
return lan966x_port_ifh_xmit(skb, ifh, dev);
}
......@@ -350,6 +384,23 @@ static int lan966x_port_get_parent_id(struct net_device *dev,
return 0;
}
static int lan966x_port_ioctl(struct net_device *dev, struct ifreq *ifr,
int cmd)
{
struct lan966x_port *port = netdev_priv(dev);
if (!phy_has_hwtstamp(dev->phydev) && port->lan966x->ptp) {
switch (cmd) {
case SIOCSHWTSTAMP:
return lan966x_ptp_hwtstamp_set(port, ifr);
case SIOCGHWTSTAMP:
return lan966x_ptp_hwtstamp_get(port, ifr);
}
}
return phy_mii_ioctl(dev->phydev, ifr, cmd);
}
static const struct net_device_ops lan966x_port_netdev_ops = {
.ndo_open = lan966x_port_open,
.ndo_stop = lan966x_port_stop,
......@@ -360,6 +411,7 @@ static const struct net_device_ops lan966x_port_netdev_ops = {
.ndo_get_stats64 = lan966x_stats_get,
.ndo_set_mac_address = lan966x_port_set_mac_address,
.ndo_get_port_parent_id = lan966x_port_get_parent_id,
.ndo_eth_ioctl = lan966x_port_ioctl,
};
bool lan966x_netdevice_check(const struct net_device *dev)
......@@ -434,6 +486,12 @@ static void lan966x_ifh_get_len(void *ifh, u64 *len)
IFH_POS_LEN, IFH_LEN * 4, UNPACK, 0);
}
static void lan966x_ifh_get_timestamp(void *ifh, u64 *timestamp)
{
packing(ifh, timestamp, IFH_POS_TIMESTAMP + IFH_WID_TIMESTAMP - 1,
IFH_POS_TIMESTAMP, IFH_LEN * 4, UNPACK, 0);
}
static irqreturn_t lan966x_xtr_irq_handler(int irq, void *args)
{
struct lan966x *lan966x = args;
......@@ -443,10 +501,10 @@ static irqreturn_t lan966x_xtr_irq_handler(int irq, void *args)
return IRQ_NONE;
do {
u64 src_port, len, timestamp;
struct net_device *dev;
struct sk_buff *skb;
int sz = 0, buf_len;
u64 src_port, len;
u32 ifh[IFH_LEN];
u32 *buf;
u32 val;
......@@ -461,6 +519,7 @@ static irqreturn_t lan966x_xtr_irq_handler(int irq, void *args)
lan966x_ifh_get_src_port(ifh, &src_port);
lan966x_ifh_get_len(ifh, &len);
lan966x_ifh_get_timestamp(ifh, &timestamp);
WARN_ON(src_port >= lan966x->num_phys_ports);
......@@ -501,6 +560,7 @@ static irqreturn_t lan966x_xtr_irq_handler(int irq, void *args)
*buf = val;
}
lan966x_ptp_rxtstamp(lan966x, skb, timestamp);
skb->protocol = eth_type_trans(skb, dev);
if (lan966x->bridge_mask & BIT(src_port))
......@@ -897,6 +957,17 @@ static int lan966x_probe(struct platform_device *pdev)
return dev_err_probe(&pdev->dev, err, "Unable to use ana irq");
}
lan966x->ptp_irq = platform_get_irq_byname(pdev, "ptp");
if (lan966x->ptp_irq > 0) {
err = devm_request_threaded_irq(&pdev->dev, lan966x->ptp_irq, NULL,
lan966x_ptp_irq_handler, IRQF_ONESHOT,
"ptp irq", lan966x);
if (err)
return dev_err_probe(&pdev->dev, err, "Unable to use ptp irq");
lan966x->ptp = 1;
}
/* init switch */
lan966x_init(lan966x);
lan966x_stats_init(lan966x);
......@@ -931,8 +1002,15 @@ static int lan966x_probe(struct platform_device *pdev)
if (err)
goto cleanup_ports;
err = lan966x_ptp_init(lan966x);
if (err)
goto cleanup_fdb;
return 0;
cleanup_fdb:
lan966x_fdb_deinit(lan966x);
cleanup_ports:
fwnode_handle_put(portnp);
......@@ -958,6 +1036,7 @@ static int lan966x_remove(struct platform_device *pdev)
lan966x_mac_purge_entries(lan966x);
lan966x_mdb_deinit(lan966x);
lan966x_fdb_deinit(lan966x);
lan966x_ptp_deinit(lan966x);
return 0;
}
......
......@@ -8,6 +8,7 @@
#include <linux/jiffies.h>
#include <linux/phy.h>
#include <linux/phylink.h>
#include <linux/ptp_clock_kernel.h>
#include <net/switchdev.h>
#include "lan966x_regs.h"
......@@ -50,6 +51,13 @@
#define LAN966X_SPEED_100 2
#define LAN966X_SPEED_10 3
#define LAN966X_PHC_COUNT 3
#define LAN966X_PHC_PORT 0
#define IFH_REW_OP_NOOP 0x0
#define IFH_REW_OP_ONE_STEP_PTP 0x3
#define IFH_REW_OP_TWO_STEP_PTP 0x4
/* MAC table entry types.
* ENTRYTYPE_NORMAL is subject to aging.
* ENTRYTYPE_LOCKED is not subject to aging.
......@@ -70,6 +78,24 @@ struct lan966x_stat_layout {
char name[ETH_GSTRING_LEN];
};
struct lan966x_phc {
struct ptp_clock *clock;
struct ptp_clock_info info;
struct hwtstamp_config hwtstamp_config;
struct lan966x *lan966x;
u8 index;
};
struct lan966x_skb_cb {
u8 rew_op;
u16 ts_id;
unsigned long jiffies;
};
#define LAN966X_PTP_TIMEOUT msecs_to_jiffies(10)
#define LAN966X_SKB_CB(skb) \
((struct lan966x_skb_cb *)((skb)->cb))
struct lan966x {
struct device *dev;
......@@ -105,6 +131,7 @@ struct lan966x {
/* interrupts */
int xtr_irq;
int ana_irq;
int ptp_irq;
/* worqueue for fdb */
struct workqueue_struct *fdb_work;
......@@ -113,6 +140,14 @@ struct lan966x {
/* mdb */
struct list_head mdb_entries;
struct list_head pgid_entries;
/* ptp */
bool ptp;
struct lan966x_phc phc[LAN966X_PHC_COUNT];
spinlock_t ptp_clock_lock; /* lock for phc */
spinlock_t ptp_ts_id_lock; /* lock for ts_id */
struct mutex ptp_lock; /* lock for ptp interface state */
u16 ptp_skbs;
};
struct lan966x_port_config {
......@@ -142,6 +177,10 @@ struct lan966x_port {
struct phylink *phylink;
struct phy *serdes;
struct fwnode_handle *fwnode;
u8 ptp_cmd;
u16 ts_id;
struct sk_buff_head tx_skbs;
};
extern const struct phylink_mac_ops lan966x_phylink_mac_ops;
......@@ -228,6 +267,18 @@ int lan966x_handle_port_mdb_del(struct lan966x_port *port,
void lan966x_mdb_erase_entries(struct lan966x *lan966x, u16 vid);
void lan966x_mdb_write_entries(struct lan966x *lan966x, u16 vid);
int lan966x_ptp_init(struct lan966x *lan966x);
void lan966x_ptp_deinit(struct lan966x *lan966x);
int lan966x_ptp_hwtstamp_set(struct lan966x_port *port, struct ifreq *ifr);
int lan966x_ptp_hwtstamp_get(struct lan966x_port *port, struct ifreq *ifr);
void lan966x_ptp_rxtstamp(struct lan966x *lan966x, struct sk_buff *skb,
u64 timestamp);
int lan966x_ptp_txtstamp_request(struct lan966x_port *port,
struct sk_buff *skb);
void lan966x_ptp_txtstamp_release(struct lan966x_port *port,
struct sk_buff *skb);
irqreturn_t lan966x_ptp_irq_handler(int irq, void *args);
static inline void __iomem *lan_addr(void __iomem *base[],
int id, int tinst, int tcnt,
int gbase, int ginst,
......
This diff is collapsed.
......@@ -19,6 +19,7 @@ enum lan966x_target {
TARGET_DEV = 13,
TARGET_GCB = 27,
TARGET_ORG = 36,
TARGET_PTP = 41,
TARGET_QS = 42,
TARGET_QSYS = 46,
TARGET_REW = 47,
......@@ -559,6 +560,108 @@ enum lan966x_target {
#define DEV_PCS1G_STICKY_LINK_DOWN_STICKY_GET(x)\
FIELD_GET(DEV_PCS1G_STICKY_LINK_DOWN_STICKY, x)
/* PTP:PTP_CFG:PTP_DOM_CFG */
#define PTP_DOM_CFG __REG(TARGET_PTP, 0, 1, 512, 0, 1, 16, 12, 0, 1, 4)
#define PTP_DOM_CFG_ENA GENMASK(11, 9)
#define PTP_DOM_CFG_ENA_SET(x)\
FIELD_PREP(PTP_DOM_CFG_ENA, x)
#define PTP_DOM_CFG_ENA_GET(x)\
FIELD_GET(PTP_DOM_CFG_ENA, x)
#define PTP_DOM_CFG_CLKCFG_DIS GENMASK(2, 0)
#define PTP_DOM_CFG_CLKCFG_DIS_SET(x)\
FIELD_PREP(PTP_DOM_CFG_CLKCFG_DIS, x)
#define PTP_DOM_CFG_CLKCFG_DIS_GET(x)\
FIELD_GET(PTP_DOM_CFG_CLKCFG_DIS, x)
/* PTP:PTP_TOD_DOMAINS:CLK_PER_CFG */
#define PTP_CLK_PER_CFG(g, r) __REG(TARGET_PTP, 0, 1, 528, g, 3, 28, 0, r, 2, 4)
/* PTP:PTP_PINS:PTP_PIN_CFG */
#define PTP_PIN_CFG(g) __REG(TARGET_PTP, 0, 1, 0, g, 8, 64, 0, 0, 1, 4)
#define PTP_PIN_CFG_PIN_ACTION GENMASK(29, 27)
#define PTP_PIN_CFG_PIN_ACTION_SET(x)\
FIELD_PREP(PTP_PIN_CFG_PIN_ACTION, x)
#define PTP_PIN_CFG_PIN_ACTION_GET(x)\
FIELD_GET(PTP_PIN_CFG_PIN_ACTION, x)
#define PTP_PIN_CFG_PIN_SYNC GENMASK(26, 25)
#define PTP_PIN_CFG_PIN_SYNC_SET(x)\
FIELD_PREP(PTP_PIN_CFG_PIN_SYNC, x)
#define PTP_PIN_CFG_PIN_SYNC_GET(x)\
FIELD_GET(PTP_PIN_CFG_PIN_SYNC, x)
#define PTP_PIN_CFG_PIN_DOM GENMASK(17, 16)
#define PTP_PIN_CFG_PIN_DOM_SET(x)\
FIELD_PREP(PTP_PIN_CFG_PIN_DOM, x)
#define PTP_PIN_CFG_PIN_DOM_GET(x)\
FIELD_GET(PTP_PIN_CFG_PIN_DOM, x)
/* PTP:PTP_PINS:PTP_TOD_SEC_MSB */
#define PTP_TOD_SEC_MSB(g) __REG(TARGET_PTP, 0, 1, 0, g, 8, 64, 4, 0, 1, 4)
#define PTP_TOD_SEC_MSB_TOD_SEC_MSB GENMASK(15, 0)
#define PTP_TOD_SEC_MSB_TOD_SEC_MSB_SET(x)\
FIELD_PREP(PTP_TOD_SEC_MSB_TOD_SEC_MSB, x)
#define PTP_TOD_SEC_MSB_TOD_SEC_MSB_GET(x)\
FIELD_GET(PTP_TOD_SEC_MSB_TOD_SEC_MSB, x)
/* PTP:PTP_PINS:PTP_TOD_SEC_LSB */
#define PTP_TOD_SEC_LSB(g) __REG(TARGET_PTP, 0, 1, 0, g, 8, 64, 8, 0, 1, 4)
/* PTP:PTP_PINS:PTP_TOD_NSEC */
#define PTP_TOD_NSEC(g) __REG(TARGET_PTP, 0, 1, 0, g, 8, 64, 12, 0, 1, 4)
#define PTP_TOD_NSEC_TOD_NSEC GENMASK(29, 0)
#define PTP_TOD_NSEC_TOD_NSEC_SET(x)\
FIELD_PREP(PTP_TOD_NSEC_TOD_NSEC, x)
#define PTP_TOD_NSEC_TOD_NSEC_GET(x)\
FIELD_GET(PTP_TOD_NSEC_TOD_NSEC, x)
/* PTP:PTP_TS_FIFO:PTP_TWOSTEP_CTRL */
#define PTP_TWOSTEP_CTRL __REG(TARGET_PTP, 0, 1, 612, 0, 1, 12, 0, 0, 1, 4)
#define PTP_TWOSTEP_CTRL_NXT BIT(11)
#define PTP_TWOSTEP_CTRL_NXT_SET(x)\
FIELD_PREP(PTP_TWOSTEP_CTRL_NXT, x)
#define PTP_TWOSTEP_CTRL_NXT_GET(x)\
FIELD_GET(PTP_TWOSTEP_CTRL_NXT, x)
#define PTP_TWOSTEP_CTRL_VLD BIT(10)
#define PTP_TWOSTEP_CTRL_VLD_SET(x)\
FIELD_PREP(PTP_TWOSTEP_CTRL_VLD, x)
#define PTP_TWOSTEP_CTRL_VLD_GET(x)\
FIELD_GET(PTP_TWOSTEP_CTRL_VLD, x)
#define PTP_TWOSTEP_CTRL_STAMP_TX BIT(9)
#define PTP_TWOSTEP_CTRL_STAMP_TX_SET(x)\
FIELD_PREP(PTP_TWOSTEP_CTRL_STAMP_TX, x)
#define PTP_TWOSTEP_CTRL_STAMP_TX_GET(x)\
FIELD_GET(PTP_TWOSTEP_CTRL_STAMP_TX, x)
#define PTP_TWOSTEP_CTRL_STAMP_PORT GENMASK(8, 1)
#define PTP_TWOSTEP_CTRL_STAMP_PORT_SET(x)\
FIELD_PREP(PTP_TWOSTEP_CTRL_STAMP_PORT, x)
#define PTP_TWOSTEP_CTRL_STAMP_PORT_GET(x)\
FIELD_GET(PTP_TWOSTEP_CTRL_STAMP_PORT, x)
#define PTP_TWOSTEP_CTRL_OVFL BIT(0)
#define PTP_TWOSTEP_CTRL_OVFL_SET(x)\
FIELD_PREP(PTP_TWOSTEP_CTRL_OVFL, x)
#define PTP_TWOSTEP_CTRL_OVFL_GET(x)\
FIELD_GET(PTP_TWOSTEP_CTRL_OVFL, x)
/* PTP:PTP_TS_FIFO:PTP_TWOSTEP_STAMP */
#define PTP_TWOSTEP_STAMP __REG(TARGET_PTP, 0, 1, 612, 0, 1, 12, 4, 0, 1, 4)
#define PTP_TWOSTEP_STAMP_STAMP_NSEC GENMASK(31, 2)
#define PTP_TWOSTEP_STAMP_STAMP_NSEC_SET(x)\
FIELD_PREP(PTP_TWOSTEP_STAMP_STAMP_NSEC, x)
#define PTP_TWOSTEP_STAMP_STAMP_NSEC_GET(x)\
FIELD_GET(PTP_TWOSTEP_STAMP_STAMP_NSEC, x)
/* DEVCPU_QS:XTR:XTR_GRP_CFG */
#define QS_XTR_GRP_CFG(r) __REG(TARGET_QS, 0, 1, 0, 0, 1, 36, 0, r, 2, 4)
......
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