Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

Merge branch 'PTP-support-for-mv88e6165-family'



Andrew Lunn says:

====================
PTP support for mv88e6165 family

The mv88e6165 family of switches supports PTP. It is however not fully
compatible with the current PTP support in the mv88e6xxx driver. This
patchset adds a level of abstraction to the PTP code, and then adds
the code needed to support the mv88e6165 family.

v2: Correctly cluster local variables in mv88e6xxx_ptp_setup()
    Added Acked-by: default avatarRichard Cochran <richardcochran@gmail.com>
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 0725345e df31b74c
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -2810,6 +2810,8 @@ static const struct mv88e6xxx_ops mv88e6161_ops = {
	.reset = mv88e6352_g1_reset,
	.vtu_getnext = mv88e6352_g1_vtu_getnext,
	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
	.avb_ops = &mv88e6165_avb_ops,
	.ptp_ops = &mv88e6165_ptp_ops,
};

static const struct mv88e6xxx_ops mv88e6165_ops = {
@@ -2838,6 +2840,8 @@ static const struct mv88e6xxx_ops mv88e6165_ops = {
	.reset = mv88e6352_g1_reset,
	.vtu_getnext = mv88e6352_g1_vtu_getnext,
	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
	.avb_ops = &mv88e6165_avb_ops,
	.ptp_ops = &mv88e6165_ptp_ops,
};

static const struct mv88e6xxx_ops mv88e6171_ops = {
@@ -3134,6 +3138,8 @@ static const struct mv88e6xxx_ops mv88e6191_ops = {
	.vtu_getnext = mv88e6390_g1_vtu_getnext,
	.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
	.serdes_power = mv88e6390_serdes_power,
	.avb_ops = &mv88e6390_avb_ops,
	.ptp_ops = &mv88e6352_ptp_ops,
};

static const struct mv88e6xxx_ops mv88e6240_ops = {
@@ -3176,6 +3182,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
	.serdes_power = mv88e6352_serdes_power,
	.gpio_ops = &mv88e6352_gpio_ops,
	.avb_ops = &mv88e6352_avb_ops,
	.ptp_ops = &mv88e6352_ptp_ops,
};

static const struct mv88e6xxx_ops mv88e6290_ops = {
@@ -3215,6 +3222,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = {
	.serdes_power = mv88e6390_serdes_power,
	.gpio_ops = &mv88e6352_gpio_ops,
	.avb_ops = &mv88e6390_avb_ops,
	.ptp_ops = &mv88e6352_ptp_ops,
};

static const struct mv88e6xxx_ops mv88e6320_ops = {
@@ -3253,6 +3261,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = {
	.vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
	.gpio_ops = &mv88e6352_gpio_ops,
	.avb_ops = &mv88e6352_avb_ops,
	.ptp_ops = &mv88e6352_ptp_ops,
};

static const struct mv88e6xxx_ops mv88e6321_ops = {
@@ -3289,6 +3298,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = {
	.vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
	.gpio_ops = &mv88e6352_gpio_ops,
	.avb_ops = &mv88e6352_avb_ops,
	.ptp_ops = &mv88e6352_ptp_ops,
};

static const struct mv88e6xxx_ops mv88e6341_ops = {
@@ -3329,6 +3339,7 @@ static const struct mv88e6xxx_ops mv88e6341_ops = {
	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
	.gpio_ops = &mv88e6352_gpio_ops,
	.avb_ops = &mv88e6390_avb_ops,
	.ptp_ops = &mv88e6352_ptp_ops,
};

static const struct mv88e6xxx_ops mv88e6350_ops = {
@@ -3402,6 +3413,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = {
	.vtu_getnext = mv88e6352_g1_vtu_getnext,
	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
	.avb_ops = &mv88e6352_avb_ops,
	.ptp_ops = &mv88e6352_ptp_ops,
};

static const struct mv88e6xxx_ops mv88e6352_ops = {
@@ -3444,6 +3456,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
	.serdes_power = mv88e6352_serdes_power,
	.gpio_ops = &mv88e6352_gpio_ops,
	.avb_ops = &mv88e6352_avb_ops,
	.ptp_ops = &mv88e6352_ptp_ops,
	.serdes_get_sset_count = mv88e6352_serdes_get_sset_count,
	.serdes_get_strings = mv88e6352_serdes_get_strings,
	.serdes_get_stats = mv88e6352_serdes_get_stats,
@@ -3488,6 +3501,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = {
	.serdes_power = mv88e6390_serdes_power,
	.gpio_ops = &mv88e6352_gpio_ops,
	.avb_ops = &mv88e6390_avb_ops,
	.ptp_ops = &mv88e6352_ptp_ops,
};

static const struct mv88e6xxx_ops mv88e6390x_ops = {
@@ -3529,6 +3543,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {
	.serdes_power = mv88e6390_serdes_power,
	.gpio_ops = &mv88e6352_gpio_ops,
	.avb_ops = &mv88e6390_avb_ops,
	.ptp_ops = &mv88e6352_ptp_ops,
};

static const struct mv88e6xxx_info mv88e6xxx_table[] = {
@@ -3680,6 +3695,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
		.pvt = true,
		.multi_chip = true,
		.tag_protocol = DSA_TAG_PROTO_EDSA,
		.ptp_support = true,
		.ops = &mv88e6161_ops,
	},

@@ -3702,6 +3718,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
		.pvt = true,
		.multi_chip = true,
		.tag_protocol = DSA_TAG_PROTO_DSA,
		.ptp_support = true,
		.ops = &mv88e6165_ops,
	},

+23 −0
Original line number Diff line number Diff line
@@ -155,6 +155,7 @@ struct mv88e6xxx_bus_ops;
struct mv88e6xxx_irq_ops;
struct mv88e6xxx_gpio_ops;
struct mv88e6xxx_avb_ops;
struct mv88e6xxx_ptp_ops;

struct mv88e6xxx_irq {
	u16 masked;
@@ -273,6 +274,7 @@ struct mv88e6xxx_chip {
	struct ptp_pin_desc	pin_config[MV88E6XXX_MAX_GPIO];
	u16 trig_config;
	u16 evcap_config;
	u16 enable_count;

	/* Per-port timestamping resources. */
	struct mv88e6xxx_port_hwtstamp port_hwtstamp[DSA_MAX_PORTS];
@@ -439,6 +441,9 @@ struct mv88e6xxx_ops {

	/* Remote Management Unit operations */
	int (*rmu_disable)(struct mv88e6xxx_chip *chip);

	/* Precision Time Protocol operations */
	const struct mv88e6xxx_ptp_ops *ptp_ops;
};

struct mv88e6xxx_irq_ops {
@@ -486,6 +491,24 @@ struct mv88e6xxx_avb_ops {
	int (*tai_write)(struct mv88e6xxx_chip *chip, int addr, u16 data);
};

struct mv88e6xxx_ptp_ops {
	u64 (*clock_read)(const struct cyclecounter *cc);
	int (*ptp_enable)(struct ptp_clock_info *ptp,
			  struct ptp_clock_request *rq, int on);
	int (*ptp_verify)(struct ptp_clock_info *ptp, unsigned int pin,
			  enum ptp_pin_function func, unsigned int chan);
	void (*event_work)(struct work_struct *ugly);
	int (*port_enable)(struct mv88e6xxx_chip *chip, int port);
	int (*port_disable)(struct mv88e6xxx_chip *chip, int port);
	int (*global_enable)(struct mv88e6xxx_chip *chip);
	int (*global_disable)(struct mv88e6xxx_chip *chip);
	int n_ext_ts;
	int arr0_sts_reg;
	int arr1_sts_reg;
	int dep_sts_reg;
	u32 rx_filters;
};

#define STATS_TYPE_PORT		BIT(0)
#define STATS_TYPE_BANK0	BIT(1)
#define STATS_TYPE_BANK1	BIT(2)
+3 −0
Original line number Diff line number Diff line
@@ -160,6 +160,7 @@
#define MV88E6390_G2_AVB_CMD_OP_WRITE		0x6000
#define MV88E6352_G2_AVB_CMD_PORT_MASK		0x0f00
#define MV88E6352_G2_AVB_CMD_PORT_TAIGLOBAL	0xe
#define MV88E6165_G2_AVB_CMD_PORT_PTPGLOBAL	0xf
#define MV88E6352_G2_AVB_CMD_PORT_PTPGLOBAL	0xf
#define MV88E6390_G2_AVB_CMD_PORT_MASK		0x1f00
#define MV88E6390_G2_AVB_CMD_PORT_TAIGLOBAL	0x1e
@@ -335,6 +336,7 @@ int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip, int target,
extern const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops;
extern const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops;

extern const struct mv88e6xxx_avb_ops mv88e6165_avb_ops;
extern const struct mv88e6xxx_avb_ops mv88e6352_avb_ops;
extern const struct mv88e6xxx_avb_ops mv88e6390_avb_ops;

@@ -484,6 +486,7 @@ static inline int mv88e6xxx_g2_pot_clear(struct mv88e6xxx_chip *chip)
static const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops = {};
static const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops = {};

static const struct mv88e6xxx_avb_ops mv88e6165_avb_ops = {};
static const struct mv88e6xxx_avb_ops mv88e6352_avb_ops = {};
static const struct mv88e6xxx_avb_ops mv88e6390_avb_ops = {};

+25 −0
Original line number Diff line number Diff line
@@ -130,6 +130,31 @@ const struct mv88e6xxx_avb_ops mv88e6352_avb_ops = {
	.tai_write		= mv88e6352_g2_avb_tai_write,
};

static int mv88e6165_g2_avb_tai_read(struct mv88e6xxx_chip *chip, int addr,
				     u16 *data, int len)
{
	return mv88e6352_g2_avb_port_ptp_read(chip,
					MV88E6165_G2_AVB_CMD_PORT_PTPGLOBAL,
					addr, data, len);
}

static int mv88e6165_g2_avb_tai_write(struct mv88e6xxx_chip *chip, int addr,
				      u16 data)
{
	return mv88e6352_g2_avb_port_ptp_write(chip,
					MV88E6165_G2_AVB_CMD_PORT_PTPGLOBAL,
					addr, data);
}

const struct mv88e6xxx_avb_ops mv88e6165_avb_ops = {
	.port_ptp_read		= mv88e6352_g2_avb_port_ptp_read,
	.port_ptp_write		= mv88e6352_g2_avb_port_ptp_write,
	.ptp_read		= mv88e6352_g2_avb_ptp_read,
	.ptp_write		= mv88e6352_g2_avb_ptp_write,
	.tai_read		= mv88e6165_g2_avb_tai_read,
	.tai_write		= mv88e6165_g2_avb_tai_write,
};

static int mv88e6390_g2_avb_port_ptp_read(struct mv88e6xxx_chip *chip,
					  int port, int addr, u16 *data,
					  int len)
+99 −35
Original line number Diff line number Diff line
@@ -51,17 +51,30 @@ static int mv88e6xxx_ptp_write(struct mv88e6xxx_chip *chip, int addr,
	return chip->info->ops->avb_ops->ptp_write(chip, addr, data);
}

static int mv88e6xxx_ptp_read(struct mv88e6xxx_chip *chip, int addr,
			      u16 *data)
{
	if (!chip->info->ops->avb_ops->ptp_read)
		return -EOPNOTSUPP;

	return chip->info->ops->avb_ops->ptp_read(chip, addr, data, 1);
}

/* TX_TSTAMP_TIMEOUT: This limits the time spent polling for a TX
 * timestamp. When working properly, hardware will produce a timestamp
 * within 1ms. Software may enounter delays due to MDIO contention, so
 * the timeout is set accordingly.
 */
#define TX_TSTAMP_TIMEOUT	msecs_to_jiffies(20)
#define TX_TSTAMP_TIMEOUT	msecs_to_jiffies(40)

int mv88e6xxx_get_ts_info(struct dsa_switch *ds, int port,
			  struct ethtool_ts_info *info)
{
	struct mv88e6xxx_chip *chip = ds->priv;
	const struct mv88e6xxx_ptp_ops *ptp_ops;
	struct mv88e6xxx_chip *chip;

	chip = ds->priv;
	ptp_ops = chip->info->ops->ptp_ops;

	if (!chip->info->ptp_support)
		return -EOPNOTSUPP;
@@ -74,17 +87,7 @@ int mv88e6xxx_get_ts_info(struct dsa_switch *ds, int port,
	info->tx_types =
		(1 << HWTSTAMP_TX_OFF) |
		(1 << HWTSTAMP_TX_ON);
	info->rx_filters =
		(1 << HWTSTAMP_FILTER_NONE) |
		(1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT) |
		(1 << HWTSTAMP_FILTER_PTP_V2_L4_SYNC) |
		(1 << HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ) |
		(1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT) |
		(1 << HWTSTAMP_FILTER_PTP_V2_L2_SYNC) |
		(1 << HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ) |
		(1 << HWTSTAMP_FILTER_PTP_V2_EVENT) |
		(1 << HWTSTAMP_FILTER_PTP_V2_SYNC) |
		(1 << HWTSTAMP_FILTER_PTP_V2_DELAY_REQ);
	info->rx_filters = ptp_ops->rx_filters;

	return 0;
}
@@ -92,10 +95,9 @@ int mv88e6xxx_get_ts_info(struct dsa_switch *ds, int port,
static int mv88e6xxx_set_hwtstamp_config(struct mv88e6xxx_chip *chip, int port,
					 struct hwtstamp_config *config)
{
	const struct mv88e6xxx_ptp_ops *ptp_ops = chip->info->ops->ptp_ops;
	struct mv88e6xxx_port_hwtstamp *ps = &chip->port_hwtstamp[port];
	bool tstamp_enable = false;
	u16 port_config0;
	int err;

	/* Prevent the TX/RX paths from trying to interact with the
	 * timestamp hardware while we reconfigure it.
@@ -120,6 +122,14 @@ static int mv88e6xxx_set_hwtstamp_config(struct mv88e6xxx_chip *chip, int port,
	/* The switch supports timestamping both L2 and L4; one cannot be
	 * disabled independently of the other.
	 */

	if (!(BIT(config->rx_filter) & ptp_ops->rx_filters)) {
		config->rx_filter = HWTSTAMP_FILTER_NONE;
		dev_dbg(chip->dev, "Unsupported rx_filter %d\n",
			config->rx_filter);
		return -ERANGE;
	}

	switch (config->rx_filter) {
	case HWTSTAMP_FILTER_NONE:
		tstamp_enable = false;
@@ -141,24 +151,22 @@ static int mv88e6xxx_set_hwtstamp_config(struct mv88e6xxx_chip *chip, int port,
		return -ERANGE;
	}

	mutex_lock(&chip->reg_lock);
	if (tstamp_enable) {
		/* Disable transportSpecific value matching, so that packets
		 * with either 1588 (0) and 802.1AS (1) will be timestamped.
		 */
		port_config0 = MV88E6XXX_PORT_PTP_CFG0_DISABLE_TSPEC_MATCH;
		chip->enable_count += 1;
		if (chip->enable_count == 1 && ptp_ops->global_enable)
			ptp_ops->global_enable(chip);
		if (ptp_ops->port_enable)
			ptp_ops->port_enable(chip, port);
	} else {
		/* Disable PTP. This disables both RX and TX timestamping. */
		port_config0 = MV88E6XXX_PORT_PTP_CFG0_DISABLE_PTP;
		if (ptp_ops->port_disable)
			ptp_ops->port_disable(chip, port);
		chip->enable_count -= 1;
		if (chip->enable_count == 0 && ptp_ops->global_disable)
			ptp_ops->global_disable(chip);
	}

	mutex_lock(&chip->reg_lock);
	err = mv88e6xxx_port_ptp_write(chip, port, MV88E6XXX_PORT_PTP_CFG0,
				       port_config0);
	mutex_unlock(&chip->reg_lock);

	if (err < 0)
		return err;

	/* Once hardware has been configured, enable timestamp checks
	 * in the RX/TX paths.
	 */
@@ -338,17 +346,18 @@ static void mv88e6xxx_get_rxts(struct mv88e6xxx_chip *chip,
static void mv88e6xxx_rxtstamp_work(struct mv88e6xxx_chip *chip,
				    struct mv88e6xxx_port_hwtstamp *ps)
{
	const struct mv88e6xxx_ptp_ops *ptp_ops = chip->info->ops->ptp_ops;
	struct sk_buff *skb;

	skb = skb_dequeue(&ps->rx_queue);

	if (skb)
		mv88e6xxx_get_rxts(chip, ps, skb, MV88E6XXX_PORT_PTP_ARR0_STS,
		mv88e6xxx_get_rxts(chip, ps, skb, ptp_ops->arr0_sts_reg,
				   &ps->rx_queue);

	skb = skb_dequeue(&ps->rx_queue2);
	if (skb)
		mv88e6xxx_get_rxts(chip, ps, skb, MV88E6XXX_PORT_PTP_ARR1_STS,
		mv88e6xxx_get_rxts(chip, ps, skb, ptp_ops->arr1_sts_reg,
				   &ps->rx_queue2);
}

@@ -389,6 +398,7 @@ bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port,
static int mv88e6xxx_txtstamp_work(struct mv88e6xxx_chip *chip,
				   struct mv88e6xxx_port_hwtstamp *ps)
{
	const struct mv88e6xxx_ptp_ops *ptp_ops = chip->info->ops->ptp_ops;
	struct skb_shared_hwtstamps shhwtstamps;
	u16 departure_block[4], status;
	struct sk_buff *tmp_skb;
@@ -401,7 +411,7 @@ static int mv88e6xxx_txtstamp_work(struct mv88e6xxx_chip *chip,

	mutex_lock(&chip->reg_lock);
	err = mv88e6xxx_port_ptp_read(chip, ps->port_id,
				      MV88E6XXX_PORT_PTP_DEP_STS,
				      ptp_ops->dep_sts_reg,
				      departure_block,
				      ARRAY_SIZE(departure_block));
	mutex_unlock(&chip->reg_lock);
@@ -425,8 +435,7 @@ static int mv88e6xxx_txtstamp_work(struct mv88e6xxx_chip *chip,

	/* We have the timestamp; go ahead and clear valid now */
	mutex_lock(&chip->reg_lock);
	mv88e6xxx_port_ptp_write(chip, ps->port_id,
				 MV88E6XXX_PORT_PTP_DEP_STS, 0);
	mv88e6xxx_port_ptp_write(chip, ps->port_id, ptp_ops->dep_sts_reg, 0);
	mutex_unlock(&chip->reg_lock);

	status = departure_block[0] & MV88E6XXX_PTP_TS_STATUS_MASK;
@@ -522,8 +531,48 @@ bool mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port,
	return true;
}

int mv88e6165_global_disable(struct mv88e6xxx_chip *chip)
{
	u16 val;
	int err;

	err = mv88e6xxx_ptp_read(chip, MV88E6165_PTP_CFG, &val);
	if (err)
		return err;
	val |= MV88E6165_PTP_CFG_DISABLE_PTP;

	return mv88e6xxx_ptp_write(chip, MV88E6165_PTP_CFG, val);
}

int mv88e6165_global_enable(struct mv88e6xxx_chip *chip)
{
	u16 val;
	int err;

	err = mv88e6xxx_ptp_read(chip, MV88E6165_PTP_CFG, &val);
	if (err)
		return err;

	val &= ~(MV88E6165_PTP_CFG_DISABLE_PTP | MV88E6165_PTP_CFG_TSPEC_MASK);

	return mv88e6xxx_ptp_write(chip, MV88E6165_PTP_CFG, val);
}

int mv88e6352_hwtstamp_port_disable(struct mv88e6xxx_chip *chip, int port)
{
	return mv88e6xxx_port_ptp_write(chip, port, MV88E6XXX_PORT_PTP_CFG0,
					MV88E6XXX_PORT_PTP_CFG0_DISABLE_PTP);
}

int mv88e6352_hwtstamp_port_enable(struct mv88e6xxx_chip *chip, int port)
{
	return mv88e6xxx_port_ptp_write(chip, port, MV88E6XXX_PORT_PTP_CFG0,
					MV88E6XXX_PORT_PTP_CFG0_DISABLE_TSPEC_MATCH);
}

static int mv88e6xxx_hwtstamp_port_setup(struct mv88e6xxx_chip *chip, int port)
{
	const struct mv88e6xxx_ptp_ops *ptp_ops = chip->info->ops->ptp_ops;
	struct mv88e6xxx_port_hwtstamp *ps = &chip->port_hwtstamp[port];

	ps->port_id = port;
@@ -531,12 +580,15 @@ static int mv88e6xxx_hwtstamp_port_setup(struct mv88e6xxx_chip *chip, int port)
	skb_queue_head_init(&ps->rx_queue);
	skb_queue_head_init(&ps->rx_queue2);

	return mv88e6xxx_port_ptp_write(chip, port, MV88E6XXX_PORT_PTP_CFG0,
					MV88E6XXX_PORT_PTP_CFG0_DISABLE_PTP);
	if (ptp_ops->port_disable)
		return ptp_ops->port_disable(chip, port);

	return 0;
}

int mv88e6xxx_hwtstamp_setup(struct mv88e6xxx_chip *chip)
{
	const struct mv88e6xxx_ptp_ops *ptp_ops = chip->info->ops->ptp_ops;
	int err;
	int i;

@@ -547,6 +599,18 @@ int mv88e6xxx_hwtstamp_setup(struct mv88e6xxx_chip *chip)
			return err;
	}

	/* Disable PTP globally */
	if (ptp_ops->global_disable) {
		err = ptp_ops->global_disable(chip);
		if (err)
			return err;
	}

	/* Set the ethertype of L2 PTP messages */
	err = mv88e6xxx_ptp_write(chip, MV88E6XXX_PTP_GC_ETYPE, ETH_P_1588);
	if (err)
		return err;

	/* MV88E6XXX_PTP_MSG_TYPE is a mask of PTP message types to
	 * timestamp. This affects all ports that have timestamping enabled,
	 * but the timestamp config is per-port; thus we configure all events
Loading