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

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
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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:
+2 −1
Original line number Diff line number Diff line
@@ -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
+34 −0
Original line number Diff line number Diff line
@@ -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)
+84 −5
Original line number Diff line number Diff line
@@ -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;
}
+51 −0
Original line number Diff line number Diff line
@@ -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,
Loading