Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH v4 1/2] dt-bindings: net: add MDIO bus multiplexer driven by a regmap device
From: Andrew Lunn @ 2019-02-06 13:52 UTC (permalink / raw)
  To: Pankaj Bansal; +Cc: Florian Fainelli, netdev@vger.kernel.org
In-Reply-To: <20190206114523.8954-2-pankaj.bansal@nxp.com>

On Wed, Feb 06, 2019 at 06:20:39AM +0000, Pankaj Bansal wrote:
> Add support for an MDIO bus multiplexer controlled by a regmap
> device, like an FPGA.
> 
> Tested on a NXP LX2160AQDS board which uses the "QIXIS" FPGA
> attached to the i2c bus.
> 
> Signed-off-by: Pankaj Bansal <pankaj.bansal@nxp.com>
> ---
> 
> Notes:
>     V4:
>     - No change
>     V3:
>     - No change
>     V2:
>     - New file describing the device tree bindings for regmap controlled devices'
>       mdio mux
> 
>  .../bindings/net/mdio-mux-regmap.txt         | 167 +++++++++++++++++
>  1 file changed, 167 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/net/mdio-mux-regmap.txt b/Documentation/devicetree/bindings/net/mdio-mux-regmap.txt
> new file mode 100644
> index 000000000000..8968f317965f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/mdio-mux-regmap.txt
> @@ -0,0 +1,167 @@
> +Properties for an MDIO bus multiplexer controlled by a regmap
> +
> +This is a special case of a MDIO bus multiplexer.  A regmap device,
> +like an FPGA, is used to control which child bus is connected.  The mdio-mux
> +node must be a child of the device that is controlled by a regmap.
> +The driver currently only supports devices with upto 32-bit registers.
> +
> +Required properties in addition to the generic multiplexer properties:
> +
> +- reg : integer, contains the offset of the register that controls the bus
> +	multiplexer. it can be 32 bit number.
> +
> +- mux-mask : integer, contains an 32 bit mask that specifies which
> +	bits in the register control the actual bus multiplexer.  The
> +	'reg' property of each child mdio-mux node must be constrained by
> +	this mask.

Hi Pankaj

Maybe you can address the comment about not having a compatible flag
by commenting that this is a device tree fragment, which is expected
to appear inside the binding of some other device. It is not a
standalone device.

	   Andrew

^ permalink raw reply

* Re: [PATCH net] virtio_net: Account for tx bytes and packets on sending xdp_frames
From: Jesper Dangaard Brouer @ 2019-02-06 13:48 UTC (permalink / raw)
  To: Saeed Mahameed
  Cc: dsahern@gmail.com, thoiland@redhat.com,
	virtualization@lists.linux-foundation.org, borkmann@iogearbox.net,
	Tariq Toukan, john.fastabend@gmail.com, mst@redhat.com,
	jakub.kicinski@netronome.com, netdev@vger.kernel.org,
	jasowang@redhat.com, davem@davemloft.net,
	makita.toshiaki@lab.ntt.co.jp, brouer
In-Reply-To: <140ecbe1e25f54f90d859cc696c4119aa96bc6eb.camel@mellanox.com>

On Wed, 6 Feb 2019 00:06:33 +0000
Saeed Mahameed <saeedm@mellanox.com> wrote:

> 3) Unrelated, In non XDP case, if skb allocation fails or driver fails
> to pass the skb up to the stack for somereason, should the driver
> increase rx packets ? IMHO the answer should be yes if we want to have
> similar behavior between XDP and non XDP cases.

I don't think "skb allocation fails" should increase rx packets
counter.  The difference is that these events are outside sysadm/users
control, and is an error detected inside the driver.  The XDP program
takes a policy choice to XDP_DROP a packet, which can be accounted
inside the XDP prog (as the samples show) or as we also discuss via a
more generic XDP-action counters.

That said, I took at quick look at driver code, and it seems this
behavior differs per driver... ixgbe and mlx5 does not count "skb
allocation fails" as RX-ed packets, while mlx4 seems to count them.


> But this could result in netdev->stats.rx_packets +
> netdev->stats.rx_dropped to be more than the actual rx-ed packets, is
> this acceptable ?

This is one reasons I think this is wrong.

--Jesper

^ permalink raw reply

* Re: [PATCH net-next v3 12/12] net: Get rid of SWITCHDEV_ATTR_ID_PORT_PARENT_ID
From: Jiri Pirko @ 2019-02-06 13:34 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: netdev, David S. Miller, Ido Schimmel, open list,
	open list:MELLANOX MLX5 core VPI driver,
	open list:NETRONOME ETHERNET DRIVERS, open list:STAGING SUBSYSTEM,
	moderated list:ETHERNET BRIDGE
In-Reply-To: <20190205235326.14600-13-f.fainelli@gmail.com>

Wed, Feb 06, 2019 at 12:53:26AM CET, f.fainelli@gmail.com wrote:
>Now that we have a dedicated NDO for getting a port's parent ID, get rid
>of SWITCHDEV_ATTR_ID_PORT_PARENT_ID and convert all callers to use the
>NDO exclusively. This is a preliminary change to getting rid of
>switchdev_ops eventually.
>
>Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>

[...]


>@@ -24,19 +23,12 @@ static int br_switchdev_mark_get(struct net_bridge *br, struct net_device *dev)
> 
> int nbp_switchdev_mark_set(struct net_bridge_port *p)
> {
>-	const struct net_device_ops *ops = p->dev->netdev_ops;
>-	struct switchdev_attr attr = {
>-		.orig_dev = p->dev,
>-		.id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
>-	};
>-	int err;
>+	struct netdev_phys_item_id ppid = { };
>+	int err = -EOPNOTSUPP;

Pointless init.


> 
> 	ASSERT_RTNL();
> 
>-	if (ops->ndo_get_port_parent_id)
>-		err = dev_get_port_parent_id(p->dev, &attr.u.ppid, true);
>-	else
>-		err = switchdev_port_attr_get(p->dev, &attr);
>+	err = dev_get_port_parent_id(p->dev, &ppid, true);
> 	if (err) {
> 		if (err == -EOPNOTSUPP)
> 			return 0;

[...]


>@@ -1146,26 +1145,17 @@ static int rtnl_phys_port_name_fill(struct sk_buff *skb, struct net_device *dev)
> 
> static int rtnl_phys_switch_id_fill(struct sk_buff *skb, struct net_device *dev)
> {
>-	const struct net_device_ops *ops = dev->netdev_ops;
>-	int err;
>-	struct switchdev_attr attr = {
>-		.orig_dev = dev,
>-		.id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID,
>-		.flags = SWITCHDEV_F_NO_RECURSE,
>-	};
>+	struct netdev_phys_item_id ppid = { };
>+	int err = -EOPNOTSUPP;

Pointless init.


> 
>-	if (ops->ndo_get_port_parent_id)
>-		err = dev_get_port_parent_id(dev, &attr.u.ppid, false);
>-	else
>-		err = switchdev_port_attr_get(dev, &attr);
>+	err = dev_get_port_parent_id(dev, &ppid, false);
> 	if (err) {
> 		if (err == -EOPNOTSUPP)
> 			return 0;
> 		return err;
> 	}
> 
>-	if (nla_put(skb, IFLA_PHYS_SWITCH_ID, attr.u.ppid.id_len,
>-		    attr.u.ppid.id))
>+	if (nla_put(skb, IFLA_PHYS_SWITCH_ID, ppid.id_len, ppid.id))
> 		return -EMSGSIZE;
> 
> 	return 0;

[...]

	
>@@ -920,15 +917,10 @@ static int vif_add(struct net *net, struct mr_table *mrt,
> 			vifc->vifc_flags | (!mrtsock ? VIFF_STATIC : 0),
> 			(VIFF_TUNNEL | VIFF_REGISTER));
> 
>-	attr.orig_dev = dev;
> 	ops = dev->netdev_ops;
>-	if (ops->ndo_get_port_parent_id &&
>-	    !dev_get_port_parent_id(dev, &attr.u.ppid, true)) {
>-		memcpy(v->dev_parent_id.id, attr.u.ppid.id, attr.u.ppid.id_len);
>-		v->dev_parent_id.id_len = attr.u.ppid.id_len;
>-	} else if (!switchdev_port_attr_get(dev, &attr)) {
>-		memcpy(v->dev_parent_id.id, attr.u.ppid.id, attr.u.ppid.id_len);
>-		v->dev_parent_id.id_len = attr.u.ppid.id_len;
>+	if (!dev_get_port_parent_id(dev, &ppid, true)) {

Please split this to:
	err = dev_get_port_parent_id(dev, &ppid, true);
	if (err) {


>+		memcpy(v->dev_parent_id.id, ppid.id, ppid.id_len);
>+		v->dev_parent_id.id_len = ppid.id_len;
> 	} else {
> 		v->dev_parent_id.id_len = 0;
> 	}

[...]


^ permalink raw reply

* Re: Is advertising of 2500Mbps support must from phy device to set phy at 2500Mbps link speed
From: Andrew Lunn @ 2019-02-06 13:38 UTC (permalink / raw)
  To: abhijit; +Cc: netdev
In-Reply-To: <ae092836-7ec1-a599-765f-6689310e97bb@gmail.com>

> Currently, we don't have any phy drivers. Generic driver doesn't seems to
> support 2500Mbps.

Correct. genphy only supports upto 1G. The c45 based genphy_c45 is
slowly gaining more features and might soon support 2.5G.

> If I have to write the driver, whether it is necessary for
> phy device to advertise speed of 2500Mbps?

The user could force it, using the ethool command you suggested. But
it is the PHY driver which configures this. If you add the driver code
to force it, you might as well add the driver code to allow it to be
negotiated.

> Phy is custom phy and is currently under test. If you know any phy device
> that supports 2500Mbps and whose data sheet is available freely please let
> me know.

There are none that i know of with open data sheets. However the IEEE
standards should be freely available and they describe the registers
the PHY is expected to have. There are also patches floating around
which add 2.5G and 5G support to the marvell10g driver. I expect these
patches to get merged soon, but maybe in a different form to make
genphy_c45 more generic.

	   Andrew

^ permalink raw reply

* Re: [PATCH net-next v3 11/12] net: dsa: Implement ndo_get_port_parent_id()
From: Jiri Pirko @ 2019-02-06 13:27 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: netdev, David S. Miller, Ido Schimmel, open list,
	open list:MELLANOX MLX5 core VPI driver,
	open list:NETRONOME ETHERNET DRIVERS, open list:STAGING SUBSYSTEM,
	moderated list:ETHERNET BRIDGE
In-Reply-To: <20190205235326.14600-12-f.fainelli@gmail.com>

Wed, Feb 06, 2019 at 12:53:25AM CET, f.fainelli@gmail.com wrote:
>DSA implements SWITCHDEV_ATTR_ID_PORT_PARENT_ID and we want to get rid
>of switchdev_ops eventually, ease that migration by implementing a
>ndo_get_port_parent_id() function which returns what
>switchdev_port_attr_get() would do.
>
>Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>

Acked-by: Jiri Pirko <jiri@mellanox.com>

^ permalink raw reply

* Re: [PATCH net-next v3 09/12] netdevsim: Implement ndo_get_port_parent_id()
From: Jiri Pirko @ 2019-02-06 13:24 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: netdev, David S. Miller, Ido Schimmel, open list,
	open list:MELLANOX MLX5 core VPI driver,
	open list:NETRONOME ETHERNET DRIVERS, open list:STAGING SUBSYSTEM,
	moderated list:ETHERNET BRIDGE
In-Reply-To: <20190205235326.14600-10-f.fainelli@gmail.com>

Wed, Feb 06, 2019 at 12:53:23AM CET, f.fainelli@gmail.com wrote:
>netdevsim only supports SWITCHDEV_ATTR_ID_PORT_PARENT_ID, which makes it a
>great candidate to be converted to use the ndo_get_port_parent_id() NDO
>instead of implementing switchdev_port_attr_get().
>
>Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>

Acked-by: Jiri Pirko <jiri@mellanox.com>

^ permalink raw reply

* Re: [PATCH net-next v3 08/12] rocker: Implement ndo_get_port_parent_id()
From: Jiri Pirko @ 2019-02-06 13:23 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: netdev, David S. Miller, Ido Schimmel, open list,
	open list:MELLANOX MLX5 core VPI driver,
	open list:NETRONOME ETHERNET DRIVERS, open list:STAGING SUBSYSTEM,
	moderated list:ETHERNET BRIDGE
In-Reply-To: <20190205235326.14600-9-f.fainelli@gmail.com>

Wed, Feb 06, 2019 at 12:53:22AM CET, f.fainelli@gmail.com wrote:
>mlxsw implements SWITCHDEV_ATTR_ID_PORT_PARENT_ID and we want to get rid
>of switchdev_ops eventually, ease that migration by implementing a
>ndo_get_port_parent_id() function which returns what
>switchdev_port_attr_get() would do.
>
>Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>

Acked-by: Jiri Pirko <jiri@mellanox.com>

^ permalink raw reply

* Re: [PATCH net-next v3 07/12] nfp: Implement ndo_get_port_parent_id()
From: Jiri Pirko @ 2019-02-06 13:23 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: netdev, David S. Miller, Ido Schimmel, open list,
	open list:MELLANOX MLX5 core VPI driver,
	open list:NETRONOME ETHERNET DRIVERS, open list:STAGING SUBSYSTEM,
	moderated list:ETHERNET BRIDGE
In-Reply-To: <20190205235326.14600-8-f.fainelli@gmail.com>

Wed, Feb 06, 2019 at 12:53:21AM CET, f.fainelli@gmail.com wrote:
>NFP only supports SWITCHDEV_ATTR_ID_PORT_PARENT_ID, which makes it a
>great candidate to be converted to use the ndo_get_port_parent_id() NDO
>instead of implementing switchdev_port_attr_get().
>
>Since NFP uses switchdev_port_same_parent_id() convert it to use
>netdev_port_same_parent_id().
>
>Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>

[...]

>@@ -31,34 +31,23 @@ struct nfp_port *nfp_port_from_netdev(struct net_device *netdev)
> 	return NULL;
> }
> 
>-static int
>-nfp_port_attr_get(struct net_device *netdev, struct switchdev_attr *attr)
>+int nfp_port_get_port_parent_id(struct net_device *netdev,
>+				struct netdev_phys_item_id *ppid)
> {
> 	struct nfp_port *port;
>+	const u8 *serial;
> 
> 	port = nfp_port_from_netdev(netdev);
> 	if (!port)
> 		return -EOPNOTSUPP;
> 
>-	switch (attr->id) {
>-	case SWITCHDEV_ATTR_ID_PORT_PARENT_ID: {
>-		const u8 *serial;
>-		/* N.B: attr->u.ppid.id is binary data */
>-		attr->u.ppid.id_len = nfp_cpp_serial(port->app->cpp, &serial);
>-		memcpy(&attr->u.ppid.id, serial, attr->u.ppid.id_len);
>-		break;
>-	}
>-	default:
>-		return -EOPNOTSUPP;
>-	}
>+	/* N.B: attr->u.ppid.id is binary data */

Comment is not updated. But I wonder if we really need it...

Otherwise this looks fine.
Acked-by: Jiri Pirko <jiri@mellanox.com>


>+	ppid->id_len = nfp_cpp_serial(port->app->cpp, &serial);
>+	memcpy(&ppid->id, serial, ppid->id_len);
> 
> 	return 0;
> }
> 
>-const struct switchdev_ops nfp_port_switchdev_ops = {
>-	.switchdev_port_attr_get	= nfp_port_attr_get,
>-};
>-
> int nfp_port_setup_tc(struct net_device *netdev, enum tc_setup_type type,
> 		      void *type_data)
> {

[...]

^ permalink raw reply

* Re: [PATCH v3 1/2] r8169: Load MAC address from device tree if present
From: Andrew Lunn @ 2019-02-06 13:29 UTC (permalink / raw)
  To: Thierry Reding
  Cc: David S. Miller, Heiner Kallweit, Joe Perches, Eric Dumazet,
	Paul Zimmerman, Michal Kubecek, Realtek linux nic maintainers,
	netdev, linux-kernel
In-Reply-To: <20190206123018.24802-1-thierry.reding@gmail.com>

On Wed, Feb 06, 2019 at 01:30:17PM +0100, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> If the system was booted using a device tree and if the device tree
> contains a MAC address, use it instead of reading one from the EEPROM.
> This is useful in situations where the EEPROM isn't properly programmed
> or where the firmware wants to override the existing MAC address.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>

Reviewed-by: Andrew Lunn <andrew@lunn.ch>

    Andrew

^ permalink raw reply

* Re: [PATCH v3 2/2] r8169: Avoid pointer aliasing
From: Andrew Lunn @ 2019-02-06 13:28 UTC (permalink / raw)
  To: Thierry Reding
  Cc: David S. Miller, Heiner Kallweit, Joe Perches, Eric Dumazet,
	Paul Zimmerman, Michal Kubecek, Realtek linux nic maintainers,
	netdev, linux-kernel
In-Reply-To: <20190206123018.24802-2-thierry.reding@gmail.com>

On Wed, Feb 06, 2019 at 01:30:18PM +0100, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> Read MAC address 32-bit at a time and manually extract the individual
> bytes. This avoids pointer aliasing and gives the compiler a better
> chance of optimizing the operation.
> 
> Suggested-by: Andrew Lunn <andrew@lunn.ch>
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
> Applies to net-next.
> 
> I tested this on a Jetson TX2 with an add-in Realtek ethernet card that
> has a properly programmed OTP to verify that I got the endianess right.
> Seems like everything works and the device behaves the same with or
> without this patch.
> 
> Changes in v3:
> - align MAC address to u16 for is_valid_ether_addr()

Hi Thierry

The point of this patch was to try to avoid the pointer aliasing,
which is what leads to the alignment requirements. But if you are
forced to align it because of is_valid_ether_addr() i would just drop
this patch. Aliasing is going to happen whatever.

But if you want to keep it.

Reviewed-by: Andrew Lunn <andrew@lunn.ch>

    Andrew

^ permalink raw reply

* Re: [PATCH net-next v3 06/12] mscc: ocelot: Implement ndo_get_port_parent_id()
From: Jiri Pirko @ 2019-02-06 13:16 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: netdev, David S. Miller, Ido Schimmel, open list,
	open list:MELLANOX MLX5 core VPI driver,
	open list:NETRONOME ETHERNET DRIVERS, open list:STAGING SUBSYSTEM,
	moderated list:ETHERNET BRIDGE
In-Reply-To: <20190205235326.14600-7-f.fainelli@gmail.com>

Wed, Feb 06, 2019 at 12:53:20AM CET, f.fainelli@gmail.com wrote:
>Ocelot only supports SWITCHDEV_ATTR_ID_PORT_PARENT_ID as a valid
>switchdev attribute getter, convert it to use ndo_get_port_parent_id()
>and get rid of the switchdev_ops::switchdev_port_attr_get altogether.
>
>Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>

Acked-by: Jiri Pirko <jiri@mellanox.com>

^ permalink raw reply

* Re: [PATCH net-next v3 05/12] mlxsw: Implement ndo_get_port_parent_id()
From: Jiri Pirko @ 2019-02-06 13:14 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: netdev, David S. Miller, Ido Schimmel, open list,
	open list:MELLANOX MLX5 core VPI driver,
	open list:NETRONOME ETHERNET DRIVERS, open list:STAGING SUBSYSTEM,
	moderated list:ETHERNET BRIDGE
In-Reply-To: <20190205235326.14600-6-f.fainelli@gmail.com>

Wed, Feb 06, 2019 at 12:53:19AM CET, f.fainelli@gmail.com wrote:
>mlxsw implements SWITCHDEV_ATTR_ID_PORT_PARENT_ID and we want to get rid
>of switchdev_ops eventually, ease that migration by implementing a
>ndo_get_port_parent_id() function which returns what
>switchdev_port_attr_get() would do.
>
>Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>

[...]

>diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
>index 2d4f213e154d..3814ba8af517 100644
>--- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c
>+++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c

Please remove net/switchdev.h inclusion from this file, you don't need
it any longer.

Otherwise, looks fine.
Acked-by: Jiri Pirko <jiri@mellanox.com>

[...]

^ permalink raw reply

* Re: Is advertising of 2500Mbps support must from phy device to set phy at 2500Mbps link speed
From: abhijit @ 2019-02-06 13:23 UTC (permalink / raw)
  To: Andrew Lunn; +Cc: netdev
In-Reply-To: <20190205145601.GJ3397@lunn.ch>

Hi Andrew,

Thank you very much for reply.

Please see my reply in-line


On Tuesday 05 February 2019 08:26 PM, Andrew Lunn wrote:
> On Tue, Feb 05, 2019 at 11:39:34AM +0530, abhijit wrote:
>> Hi All,
>>
>> We are using Ethernet MAC which has integrated Phy. This phy supports speed
>> up to 10000Mbps. The phy has, 1000Base-X PCS(Physical Coding Sub-layer)
>> followed by SerDes interface to support 10Mbps to 10000Mbps. Currently we
>> are trying to get this phy at 2500Mbps. This device has 16 registers that
>> corresponds to Clause 37, which can be used to advertise speed till
>> 1000Mbps.
>> So my question is,
>> 1. Without phy advertising its capability of 2500Mbps, is there any way I
>> can set phy speed at 2500Mbps?
>> 2. I tried disabling auto-negotiation and setting speed at 2500Mbps with
>> ethtool (ethtool -s eth0  speed 2500 autoneg off), but the ethtool reported
>> this configuration as invalid?
>> 3. At the end we are targeting print of "link up (2500/Full)"
> Hi Abhijit
>
> It all depends on what the PHY driver can do.
> It sounds like the PHY
> driver does not support multi-gige speeds. So you probably need to
> work on the PHY driver and add support for them.
Currently, we don't have any phy drivers. Generic driver doesn't seems 
to support 2500Mbps. If I have to write the driver, whether it is 
necessary for phy device to advertise speed of 2500Mbps?
>
> What PHY is it?
Phy is custom phy and is currently under test. If you know any phy 
device that supports 2500Mbps and whose data sheet is available freely 
please let me know.
>
>       Andrew


^ permalink raw reply

* [PATCH v2 04/28] thunderbolt: Add dummy read after port capability list walk on Light Ridge
From: Mika Westerberg @ 2019-02-06 13:17 UTC (permalink / raw)
  To: linux-kernel
  Cc: Michael Jamet, Yehezkel Bernat, Andreas Noever, Lukas Wunner,
	David S . Miller, Mika Westerberg, Andy Shevchenko, netdev
In-Reply-To: <20190206131738.43696-1-mika.westerberg@linux.intel.com>

Light Ridge has an issue where reading the next capability pointer
location in port config space the read data is not cleared. It is fine
to read capabilities each after another so only thing we need to do is
to make sure we issue dummy read after tb_port_find_cap() is finished to
avoid the issue in next read.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/cap.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/drivers/thunderbolt/cap.c b/drivers/thunderbolt/cap.c
index 0de548bda663..8aceb0d97a63 100644
--- a/drivers/thunderbolt/cap.c
+++ b/drivers/thunderbolt/cap.c
@@ -57,6 +57,21 @@ static int tb_port_enable_tmu(struct tb_port *port, bool enable)
 	return tb_sw_write(sw, &value, TB_CFG_SWITCH, offset, 1);
 }
 
+static void tb_port_dummy_read(struct tb_port *port)
+{
+	/*
+	 * When reading from next capability pointer location in port
+	 * config space the read data is not cleared on LR. To avoid
+	 * reading stale data on next read perform one dummy read after
+	 * port capabilities are walked.
+	 */
+	if (port->sw->config.device_id == PCI_DEVICE_ID_INTEL_LIGHT_RIDGE) {
+		u32 dummy;
+
+		tb_port_read(port, &dummy, TB_CFG_PORT, 0, 1);
+	}
+}
+
 static int __tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap)
 {
 	u32 offset = 1;
@@ -97,6 +112,7 @@ int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap)
 
 	ret = __tb_port_find_cap(port, cap);
 
+	tb_port_dummy_read(port);
 	tb_port_enable_tmu(port, false);
 
 	return ret;
-- 
2.20.1


^ permalink raw reply related

* [PATCH v2 00/28] thunderbolt: Software connection manager improvements
From: Mika Westerberg @ 2019-02-06 13:17 UTC (permalink / raw)
  To: linux-kernel
  Cc: Michael Jamet, Yehezkel Bernat, Andreas Noever, Lukas Wunner,
	David S . Miller, Mika Westerberg, Andy Shevchenko, netdev

Hi,

Software connection manager (drivers/thunderbolt/tb.c) is used on older
Apple hardware with Light Ridge, Cactus Ridge or Falcon Ridge controllers
to create PCIe tunnels when a Thunderbolt device is connected. Currently
only one PCIe tunnel is supported. On newer Alpine Ridge based Apple
systems the driver starts the firmware which then takes care creating
tunnels.

This series improves the software connection manager so that it will
support:

  - Full PCIe daisy chains (up to 6 devices)
  - Display Port tunneling
  - P2P networking

We also add support for Titan Ridge based Apple systems where we can use
the same flows than with Alpine Ridge to start the firmware.

Note in order to prevent possible DMA attacks on these systems we should
make sure IOMMU is enabled. One option is to force dmar_platform_optin()
return true on Apple hardware. However, it is not part of this series. I'm
trusting people using Linux on such systems to take care of it.  :-)

Previous version of the patch series can be viewed here:

  https://lkml.org/lkml/2019/1/29/924

Changes from v1:

  * Added ACK from David

  * Add constant (TMU_ACCESS_EN) for BIT(20) when TMU access is enabled. We
    keep it in cap.c close to the LR/ER workaround. Also we enable/disable
    only during capability walk. If it turns we need to have it enabled
    elsewhere we can move it to switch.c and enable just once during
    switch enumeration.

  * Use 0 to mean no cap_adap instead of negative value. This follows
    cap_phy.

  * Use correct PCI IDs (_BRIDGE) in the last patch where we start firmware
    on Titan Ridge. It wrongly used NHI PCI IDs in v1.

Mika Westerberg (28):
  net: thunderbolt: Unregister ThunderboltIP protocol handler when suspending
  thunderbolt: Do not allocate switch if depth is greater than 6
  thunderbolt: Enable TMU access when accessing port space on legacy devices
  thunderbolt: Add dummy read after port capability list walk on Light Ridge
  thunderbolt: Move LC specific functionality into a separate file
  thunderbolt: Configure lanes when switch is initialized
  thunderbolt: Set sleep bit when suspending switch
  thunderbolt: Properly disable path
  thunderbolt: Cache adapter specific capability offset into struct port
  thunderbolt: Rename tunnel_pci to tunnel
  thunderbolt: Generalize tunnel creation functionality
  thunderbolt: Add functions for allocating and releasing hop IDs
  thunderbolt: Add helper function to iterate from one port to another
  thunderbolt: Extend tunnel creation to more than 2 adjacent switches
  thunderbolt: Deactivate all paths before restarting them
  thunderbolt: Discover preboot PCIe paths the boot firmware established
  thunderbolt: Add support for full PCIe daisy chains
  thunderbolt: Scan only valid NULL adapter ports in hotplug
  thunderbolt: Generalize port finding routines to support all port types
  thunderbolt: Rework NFC credits handling
  thunderbolt: Add support for Display Port tunnels
  thunderbolt: Run tb_xdp_handle_request() in system workqueue
  thunderbolt: Add XDomain UUID exchange support
  thunderbolt: Add support for DMA tunnels
  thunderbolt: Make tb_switch_alloc() return ERR_PTR()
  thunderbolt: Add support for XDomain connections
  thunderbolt: Make rest of the logging to happen at debug level
  thunderbolt: Start firmware on Titan Ridge Apple systems

 drivers/net/thunderbolt.c        |   3 +
 drivers/thunderbolt/Makefile     |   4 +-
 drivers/thunderbolt/cap.c        |  90 +++-
 drivers/thunderbolt/ctl.c        |   2 +-
 drivers/thunderbolt/icm.c        |  15 +-
 drivers/thunderbolt/lc.c         | 179 ++++++++
 drivers/thunderbolt/path.c       | 326 +++++++++++++--
 drivers/thunderbolt/switch.c     | 466 ++++++++++++++++++---
 drivers/thunderbolt/tb.c         | 529 ++++++++++++++++++------
 drivers/thunderbolt/tb.h         |  67 ++-
 drivers/thunderbolt/tb_msgs.h    |  11 +
 drivers/thunderbolt/tb_regs.h    |  50 ++-
 drivers/thunderbolt/tunnel.c     | 681 +++++++++++++++++++++++++++++++
 drivers/thunderbolt/tunnel.h     |  75 ++++
 drivers/thunderbolt/tunnel_pci.c | 226 ----------
 drivers/thunderbolt/tunnel_pci.h |  31 --
 drivers/thunderbolt/xdomain.c    | 142 ++++++-
 include/linux/thunderbolt.h      |   8 +
 18 files changed, 2389 insertions(+), 516 deletions(-)
 create mode 100644 drivers/thunderbolt/lc.c
 create mode 100644 drivers/thunderbolt/tunnel.c
 create mode 100644 drivers/thunderbolt/tunnel.h
 delete mode 100644 drivers/thunderbolt/tunnel_pci.c
 delete mode 100644 drivers/thunderbolt/tunnel_pci.h

-- 
2.20.1


^ permalink raw reply

* [PATCH v2 03/28] thunderbolt: Enable TMU access when accessing port space on legacy devices
From: Mika Westerberg @ 2019-02-06 13:17 UTC (permalink / raw)
  To: linux-kernel
  Cc: Michael Jamet, Yehezkel Bernat, Andreas Noever, Lukas Wunner,
	David S . Miller, Mika Westerberg, Andy Shevchenko, netdev
In-Reply-To: <20190206131738.43696-1-mika.westerberg@linux.intel.com>

Light Ridge and Eagle Ridge both need to have TMU access enabled before
port space can be fully accessed so make sure it happens on those. This
allows us to get rid of the offset quirk in tb_port_find_cap().

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/cap.c | 74 ++++++++++++++++++++++++++++++---------
 1 file changed, 57 insertions(+), 17 deletions(-)

diff --git a/drivers/thunderbolt/cap.c b/drivers/thunderbolt/cap.c
index 9553305c63ea..0de548bda663 100644
--- a/drivers/thunderbolt/cap.c
+++ b/drivers/thunderbolt/cap.c
@@ -13,6 +13,7 @@
 
 #define CAP_OFFSET_MAX		0xff
 #define VSE_CAP_OFFSET_MAX	0xffff
+#define TMU_ACCESS_EN		BIT(20)
 
 struct tb_cap_any {
 	union {
@@ -22,28 +23,43 @@ struct tb_cap_any {
 	};
 } __packed;
 
-/**
- * tb_port_find_cap() - Find port capability
- * @port: Port to find the capability for
- * @cap: Capability to look
- *
- * Returns offset to start of capability or %-ENOENT if no such
- * capability was found. Negative errno is returned if there was an
- * error.
- */
-int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap)
+static int tb_port_enable_tmu(struct tb_port *port, bool enable)
 {
-	u32 offset;
+	struct tb_switch *sw = port->sw;
+	u32 value, offset;
+	int ret;
 
 	/*
-	 * DP out adapters claim to implement TMU capability but in
-	 * reality they do not so we hard code the adapter specific
-	 * capability offset here.
+	 * Legacy devices need to have TMU access enabled before port
+	 * space can be fully accessed.
 	 */
-	if (port->config.type == TB_TYPE_DP_HDMI_OUT)
-		offset = 0x39;
+	switch (sw->config.device_id) {
+	case PCI_DEVICE_ID_INTEL_LIGHT_RIDGE:
+		offset = 0x26;
+		break;
+	case PCI_DEVICE_ID_INTEL_EAGLE_RIDGE:
+		offset = 0x2a;
+		break;
+
+	default:
+		return 0;
+	}
+
+	ret = tb_sw_read(sw, &value, TB_CFG_SWITCH, offset, 1);
+	if (ret)
+		return ret;
+
+	if (enable)
+		value |= TMU_ACCESS_EN;
 	else
-		offset = 0x1;
+		value &= ~TMU_ACCESS_EN;
+
+	return tb_sw_write(sw, &value, TB_CFG_SWITCH, offset, 1);
+}
+
+static int __tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap)
+{
+	u32 offset = 1;
 
 	do {
 		struct tb_cap_any header;
@@ -62,6 +78,30 @@ int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap)
 	return -ENOENT;
 }
 
+/**
+ * tb_port_find_cap() - Find port capability
+ * @port: Port to find the capability for
+ * @cap: Capability to look
+ *
+ * Returns offset to start of capability or %-ENOENT if no such
+ * capability was found. Negative errno is returned if there was an
+ * error.
+ */
+int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap)
+{
+	int ret;
+
+	ret = tb_port_enable_tmu(port, true);
+	if (ret)
+		return ret;
+
+	ret = __tb_port_find_cap(port, cap);
+
+	tb_port_enable_tmu(port, false);
+
+	return ret;
+}
+
 static int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap)
 {
 	int offset = sw->config.first_cap_offset;
-- 
2.20.1


^ permalink raw reply related

* [PATCH v2 08/28] thunderbolt: Properly disable path
From: Mika Westerberg @ 2019-02-06 13:17 UTC (permalink / raw)
  To: linux-kernel
  Cc: Michael Jamet, Yehezkel Bernat, Andreas Noever, Lukas Wunner,
	David S . Miller, Mika Westerberg, Andy Shevchenko, netdev
In-Reply-To: <20190206131738.43696-1-mika.westerberg@linux.intel.com>

We need to wait until all buffers have been drained before the path can
be considered disabled. Do this for every hop in a path. Also if the
switch is physically disconnected, do not bother disabling it anymore
(it is not present anyway).

This adds another bit field to struct tb_regs_hop even if we are trying
to get rid of them but we can clean them up another day.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/path.c    | 44 ++++++++++++++++++++++++++++++++---
 drivers/thunderbolt/tb_regs.h |  3 ++-
 2 files changed, 43 insertions(+), 4 deletions(-)

diff --git a/drivers/thunderbolt/path.c b/drivers/thunderbolt/path.c
index a11956522bac..48cb15ff4446 100644
--- a/drivers/thunderbolt/path.c
+++ b/drivers/thunderbolt/path.c
@@ -7,6 +7,8 @@
 
 #include <linux/slab.h>
 #include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/ktime.h>
 
 #include "tb.h"
 
@@ -74,13 +76,49 @@ static void __tb_path_deallocate_nfc(struct tb_path *path, int first_hop)
 	}
 }
 
+static int __tb_path_deactivate_hop(struct tb_port *port, int hop_index)
+{
+	struct tb_regs_hop hop;
+	ktime_t timeout;
+	int ret;
+
+	if (port->sw->is_unplugged)
+		return 0;
+
+	/* Disable the path */
+	ret = tb_port_read(port, &hop, TB_CFG_HOPS, 2 * hop_index, 2);
+	if (ret)
+		return ret;
+
+	hop.enable = 0;
+
+	ret = tb_port_write(port, &hop, TB_CFG_HOPS, 2 * hop_index, 2);
+	if (ret)
+		return ret;
+
+	/* Wait until it is drained */
+	timeout = ktime_add_ms(ktime_get(), 500);
+	do {
+		ret = tb_port_read(port, &hop, TB_CFG_HOPS, 2 * hop_index, 2);
+		if (ret)
+			return ret;
+
+		if (!hop.pending)
+			return 0;
+
+		usleep_range(10, 20);
+	} while (ktime_before(ktime_get(), timeout));
+
+	return -ETIMEDOUT;
+}
+
 static void __tb_path_deactivate_hops(struct tb_path *path, int first_hop)
 {
 	int i, res;
-	struct tb_regs_hop hop = { };
+
 	for (i = first_hop; i < path->path_length; i++) {
-		res = tb_port_write(path->hops[i].in_port, &hop, TB_CFG_HOPS,
-				    2 * path->hops[i].in_hop_index, 2);
+		res = __tb_path_deactivate_hop(path->hops[i].in_port,
+					       path->hops[i].in_hop_index);
 		if (res)
 			tb_port_warn(path->hops[i].in_port,
 				     "hop deactivation failed for hop %d, index %d\n",
diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h
index 1ab6e0fb31c0..82ac4ec8757f 100644
--- a/drivers/thunderbolt/tb_regs.h
+++ b/drivers/thunderbolt/tb_regs.h
@@ -234,7 +234,8 @@ struct tb_regs_hop {
 	bool egress_fc:1;
 	bool ingress_shared_buffer:1;
 	bool egress_shared_buffer:1;
-	u32 unknown3:4; /* set to zero */
+	bool pending:1;
+	u32 unknown3:3; /* set to zero */
 } __packed;
 
 /* Common link controller registers */
-- 
2.20.1


^ permalink raw reply related

* [PATCH v2 05/28] thunderbolt: Move LC specific functionality into a separate file
From: Mika Westerberg @ 2019-02-06 13:17 UTC (permalink / raw)
  To: linux-kernel
  Cc: Michael Jamet, Yehezkel Bernat, Andreas Noever, Lukas Wunner,
	David S . Miller, Mika Westerberg, Andy Shevchenko, netdev
In-Reply-To: <20190206131738.43696-1-mika.westerberg@linux.intel.com>

We will be adding more link controller functionality in subsequent
patches and it does not make sense to keep all that in switch.c, so
separate LC functionality into its own file.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/Makefile  |  2 +-
 drivers/thunderbolt/lc.c      | 21 +++++++++++++++++++++
 drivers/thunderbolt/switch.c  | 12 +++++++-----
 drivers/thunderbolt/tb.h      |  3 +++
 drivers/thunderbolt/tb_regs.h |  2 ++
 5 files changed, 34 insertions(+), 6 deletions(-)
 create mode 100644 drivers/thunderbolt/lc.c

diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile
index f2f0de27252b..8531f15d3b3c 100644
--- a/drivers/thunderbolt/Makefile
+++ b/drivers/thunderbolt/Makefile
@@ -1,3 +1,3 @@
 obj-${CONFIG_THUNDERBOLT} := thunderbolt.o
 thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel_pci.o eeprom.o
-thunderbolt-objs += domain.o dma_port.o icm.o property.o xdomain.o
+thunderbolt-objs += domain.o dma_port.o icm.o property.o xdomain.o lc.o
diff --git a/drivers/thunderbolt/lc.c b/drivers/thunderbolt/lc.c
new file mode 100644
index 000000000000..2134a55ed837
--- /dev/null
+++ b/drivers/thunderbolt/lc.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Thunderbolt link controller support
+ *
+ * Copyright (C) 2019, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
+ */
+
+#include "tb.h"
+
+/**
+ * tb_lc_read_uuid() - Read switch UUID from link controller common register
+ * @sw: Switch whose UUID is read
+ * @uuid: UUID is placed here
+ */
+int tb_lc_read_uuid(struct tb_switch *sw, u32 *uuid)
+{
+	if (!sw->cap_lc)
+		return -EINVAL;
+	return tb_sw_read(sw, uuid, TB_CFG_SWITCH, sw->cap_lc + TB_LC_FUSE, 4);
+}
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index a90d21abed88..bd96eebd8248 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -1207,6 +1207,10 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, struct device *parent,
 	}
 	sw->cap_plug_events = cap;
 
+	cap = tb_switch_find_vse_cap(sw, TB_VSE_CAP_LINK_CONTROLLER);
+	if (cap > 0)
+		sw->cap_lc = cap;
+
 	/* Root switch is always authorized */
 	if (!route)
 		sw->authorized = true;
@@ -1303,7 +1307,7 @@ int tb_switch_configure(struct tb_switch *sw)
 static void tb_switch_set_uuid(struct tb_switch *sw)
 {
 	u32 uuid[4];
-	int cap;
+	int ret;
 
 	if (sw->uuid)
 		return;
@@ -1312,10 +1316,8 @@ static void tb_switch_set_uuid(struct tb_switch *sw)
 	 * The newer controllers include fused UUID as part of link
 	 * controller specific registers
 	 */
-	cap = tb_switch_find_vse_cap(sw, TB_VSE_CAP_LINK_CONTROLLER);
-	if (cap > 0) {
-		tb_sw_read(sw, uuid, TB_CFG_SWITCH, cap + 3, 4);
-	} else {
+	ret = tb_lc_read_uuid(sw, uuid);
+	if (ret) {
 		/*
 		 * ICM generates UUID based on UID and fills the upper
 		 * two words with ones. This is not strictly following
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 5faec5a8eb98..530464b25dcb 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -63,6 +63,7 @@ struct tb_switch_nvm {
  * @device_name: Name of the device (or %NULL if not known)
  * @generation: Switch Thunderbolt generation
  * @cap_plug_events: Offset to the plug events capability (%0 if not found)
+ * @cap_lc: Offset to the link controller capability (%0 if not found)
  * @is_unplugged: The switch is going away
  * @drom: DROM of the switch (%NULL if not found)
  * @nvm: Pointer to the NVM if the switch has one (%NULL otherwise)
@@ -98,6 +99,7 @@ struct tb_switch {
 	const char *device_name;
 	unsigned int generation;
 	int cap_plug_events;
+	int cap_lc;
 	bool is_unplugged;
 	u8 *drom;
 	struct tb_switch_nvm *nvm;
@@ -448,6 +450,7 @@ bool tb_path_is_invalid(struct tb_path *path);
 int tb_drom_read(struct tb_switch *sw);
 int tb_drom_read_uid_only(struct tb_switch *sw, u64 *uid);
 
+int tb_lc_read_uuid(struct tb_switch *sw, u32 *uuid);
 
 static inline int tb_route_length(u64 route)
 {
diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h
index 6f1ff04ee195..4895ae9f0b40 100644
--- a/drivers/thunderbolt/tb_regs.h
+++ b/drivers/thunderbolt/tb_regs.h
@@ -237,5 +237,7 @@ struct tb_regs_hop {
 	u32 unknown3:4; /* set to zero */
 } __packed;
 
+/* Common link controller registers */
+#define TB_LC_FUSE			0x03
 
 #endif
-- 
2.20.1


^ permalink raw reply related

* Re: [PATCH v2 4/6] ethtool: support per-queue sub command --show-coalesce
From: Michal Kubecek @ 2019-02-06 13:22 UTC (permalink / raw)
  To: netdev; +Cc: Jeff Kirsher, linville, Nicholas Nunley, nhorman, sassmann
In-Reply-To: <20190206000106.24364-4-jeffrey.t.kirsher@intel.com>

On Tue, Feb 05, 2019 at 04:01:04PM -0800, Jeff Kirsher wrote:
> diff --git a/ethtool.c b/ethtool.c
> index 4dc725c..9a1b83b 100644
> --- a/ethtool.c
> +++ b/ethtool.c
> @@ -1409,6 +1409,29 @@ static int dump_coalesce(const struct ethtool_coalesce *ecoal)
>  	return 0;
>  }
>  
> +void dump_per_queue_coalesce(struct ethtool_per_queue_op *per_queue_opt,
> +			     __u32 *queue_mask)
> +{
> +	char *addr;
> +	int i;
> +
> +	addr = (char *)per_queue_opt + sizeof(*per_queue_opt);
> +	for (i = 0; i < __KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32); i++) {
> +		int queue = i * 32;
> +		__u32 mask = queue_mask[i];
> +
> +		while (mask > 0) {
> +			if (mask & 0x1) {
> +				fprintf(stdout, "Queue: %d\n", queue);
> +				dump_coalesce((struct ethtool_coalesce *)addr);
> +				addr += sizeof(struct ethtool_coalesce);
> +			}
> +			mask = mask >> 1;
> +			queue++;
> +		}
> +	}
> +}
> +
>  struct feature_state {
>  	u32 off_flags;
>  	struct ethtool_gfeatures features;

The casts and pointer arithmetic are a bit complicated. How about this?

	struct ethtool_coalesce *addr;
...
	addr = (struct ethtool_coalesce *)(per_queue_opt + 1);
...
	dump_coalesce(addr);
	addr++;

Also passing n_queue down from do_perqueue() would prevent having to
parse the whole bitmap even if we already know the NIC has only few
queues.

> @@ -5245,7 +5268,8 @@ static const struct option {
>  	{ "--show-fec", 1, do_gfec, "Show FEC settings"},
>  	{ "--set-fec", 1, do_sfec, "Set FEC settings",
>  	  "		[ encoding auto|off|rs|baser [...]]\n"},
> -	{ "--set-perqueue-command", 1, do_perqueue, "Set per queue command",
> +	{ "--set-perqueue-command", 1, do_perqueue, "Set per queue command. "
> +	  "The supported sub commands include --show-coalesce",
>  	  "             [queue_mask %x] SUB_COMMAND\n"},
>  	{ "-h|--help", 0, show_usage, "Show this help" },
>  	{ "--version", 0, do_version, "Show version number" },
> @@ -5350,8 +5374,32 @@ static int find_max_num_queues(struct cmd_context *ctx)
>  		   echannels.combined_count);
>  }
>  
> +static struct ethtool_per_queue_op *
> +get_per_queue_coalesce(struct cmd_context *ctx, __u32 *queue_mask, int n_queues)
> +{
> +	struct ethtool_per_queue_op *per_queue_opt;
> +
> +	per_queue_opt = malloc(sizeof(*per_queue_opt) + n_queues *
> +			sizeof(struct ethtool_coalesce));
> +	if (!per_queue_opt)
> +		return NULL;
> +
> +	memcpy(per_queue_opt->queue_mask, queue_mask,
> +	       __KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32) * sizeof(__u32));
> +	per_queue_opt->cmd = ETHTOOL_PERQUEUE;
> +	per_queue_opt->sub_command = ETHTOOL_GCOALESCE;
> +	if (send_ioctl(ctx, per_queue_opt)) {
> +		free(per_queue_opt);
> +		perror("Cannot get device per queue parameters");
> +		return NULL;
> +	}
> +
> +	return per_queue_opt;
> +}
> +
>  static int do_perqueue(struct cmd_context *ctx)
>  {
> +	struct ethtool_per_queue_op *per_queue_opt;
>  	__u32 queue_mask[__KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32)] = {0};
>  	int i, n_queues = 0;
>  
> @@ -5390,7 +5438,19 @@ static int do_perqueue(struct cmd_context *ctx)
>  	if (i < 0)
>  		exit_bad_args();
>  
> -	/* no sub_command support yet */
> +	if (strstr(args[i].opts, "--show-coalesce") != NULL) {

Comparing args[i].func to do_gcoalesce might be easier.

> +		per_queue_opt = get_per_queue_coalesce(ctx, queue_mask,
> +						       n_queues);
> +		if (per_queue_opt == NULL) {
> +			perror("Cannot get device per queue parameters");
> +			return -EFAULT;
> +		}
> +		dump_per_queue_coalesce(per_queue_opt, queue_mask);
> +		free(per_queue_opt);
> +	} else {
> +		perror("The subcommand is not supported yet");
> +		return -EOPNOTSUPP;
> +	}
>  
>  	return 0;
>  }

Michal Kubecek

^ permalink raw reply

* [PATCH v2 12/28] thunderbolt: Add functions for allocating and releasing hop IDs
From: Mika Westerberg @ 2019-02-06 13:17 UTC (permalink / raw)
  To: linux-kernel
  Cc: Michael Jamet, Yehezkel Bernat, Andreas Noever, Lukas Wunner,
	David S . Miller, Mika Westerberg, Andy Shevchenko, netdev
In-Reply-To: <20190206131738.43696-1-mika.westerberg@linux.intel.com>

Each port has a separate path configuration space that is used for
finding the next hop (switch) in the path. Hop ID is an index to this
configuration space and hop IDs 0 - 7 are reserved.

In order to get next available hop ID for each direction we provide two
pairs of helper functions that can be used to allocate and release hop
IDs for a given port.

While there remove obsolete TODO comment.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/switch.c | 87 +++++++++++++++++++++++++++++++++++-
 drivers/thunderbolt/tb.h     |  8 ++++
 2 files changed, 94 insertions(+), 1 deletion(-)

diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index b20af050ce9a..320f64ebe8b8 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -601,11 +601,88 @@ static int tb_init_port(struct tb_port *port)
 
 	tb_dump_port(port->sw->tb, &port->config);
 
-	/* TODO: Read dual link port, DP port and more from EEPROM. */
+	/* Control port does not need Hop ID allocation */
+	if (port->port) {
+		ida_init(&port->in_hopids);
+		ida_init(&port->out_hopids);
+	}
+
 	return 0;
 
 }
 
+static int tb_port_alloc_hopid(struct tb_port *port, bool in, int min_hopid,
+			       int max_hopid)
+{
+	int port_max_hopid;
+	struct ida *ida;
+
+	if (in) {
+		port_max_hopid = port->config.max_in_hop_id;
+		ida = &port->in_hopids;
+	} else {
+		port_max_hopid = port->config.max_out_hop_id;
+		ida = &port->out_hopids;
+	}
+
+	/* Hop IDs 0-7 are reserved */
+	if (min_hopid < 8)
+		min_hopid = 8;
+
+	if (max_hopid < 0 || max_hopid > port_max_hopid)
+		max_hopid = port_max_hopid;
+
+	return ida_simple_get(ida, min_hopid, max_hopid + 1, GFP_KERNEL);
+}
+
+/**
+ * tb_port_alloc_in_hopid() - Allocate input hop ID from port
+ * @port: Port to allocate hop ID for
+ * @min_hopid: Minimum acceptable input hop ID
+ * @max_hopid: Maximum acceptable input hop ID
+ *
+ * Return: Hop ID between @min_hopid and @max_hopid or negative errno in
+ * case of error.
+ */
+int tb_port_alloc_in_hopid(struct tb_port *port, int min_hopid, int max_hopid)
+{
+	return tb_port_alloc_hopid(port, true, min_hopid, max_hopid);
+}
+
+/**
+ * tb_port_alloc_out_hopid() - Allocate output hop ID from port
+ * @port: Port to allocate hop ID for
+ * @min_hopid: Minimum acceptable output hop ID
+ * @max_hopid: Maximum acceptable output hop ID
+ *
+ * Return: Hop ID between @min_hopid and @max_hopid or negative errno in
+ * case of error.
+ */
+int tb_port_alloc_out_hopid(struct tb_port *port, int min_hopid, int max_hopid)
+{
+	return tb_port_alloc_hopid(port, false, min_hopid, max_hopid);
+}
+
+/**
+ * tb_port_release_in_hopid() - Release allocated input hop ID from port
+ * @port: Port whose hop ID to release
+ * @hopid: Hop ID to release
+ */
+void tb_port_release_in_hopid(struct tb_port *port, int hopid)
+{
+	ida_simple_remove(&port->in_hopids, hopid);
+}
+
+/**
+ * tb_port_release_out_hopid() - Release allocated output hop ID from port
+ * @port: Port whose hop ID to release
+ * @hopid: Hop ID to release
+ */
+void tb_port_release_out_hopid(struct tb_port *port, int hopid)
+{
+	ida_simple_remove(&port->out_hopids, hopid);
+}
+
 /**
  * tb_pci_port_enable() - Enable PCIe adapter port
  * @port: PCIe port to enable
@@ -1080,9 +1157,17 @@ static const struct attribute_group *switch_groups[] = {
 static void tb_switch_release(struct device *dev)
 {
 	struct tb_switch *sw = tb_to_switch(dev);
+	int i;
 
 	dma_port_free(sw->dma_port);
 
+	for (i = 1; i <= sw->config.max_port_number; i++) {
+		if (!sw->ports[i].disabled) {
+			ida_destroy(&sw->ports[i].in_hopids);
+			ida_destroy(&sw->ports[i].out_hopids);
+		}
+	}
+
 	kfree(sw->uuid);
 	kfree(sw->device_name);
 	kfree(sw->vendor_name);
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index a13d1cd53bc3..bfa1cee193fd 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -130,6 +130,8 @@ struct tb_switch {
  * @dual_link_port: If the switch is connected using two ports, points
  *		    to the other port.
  * @link_nr: Is this primary or secondary port on the dual_link.
+ * @in_hopids: Currently allocated input hopids
+ * @out_hopids: Currently allocated output hopids
  */
 struct tb_port {
 	struct tb_regs_port_header config;
@@ -142,6 +144,8 @@ struct tb_port {
 	bool disabled;
 	struct tb_port *dual_link_port;
 	u8 link_nr:1;
+	struct ida in_hopids;
+	struct ida out_hopids;
 };
 
 /**
@@ -439,6 +443,10 @@ static inline struct tb_switch *tb_to_switch(struct device *dev)
 int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged);
 int tb_port_add_nfc_credits(struct tb_port *port, int credits);
 int tb_port_clear_counter(struct tb_port *port, int counter);
+int tb_port_alloc_in_hopid(struct tb_port *port, int hopid, int max_hopid);
+void tb_port_release_in_hopid(struct tb_port *port, int hopid);
+int tb_port_alloc_out_hopid(struct tb_port *port, int hopid, int max_hopid);
+void tb_port_release_out_hopid(struct tb_port *port, int hopid);
 
 int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec);
 int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap);
-- 
2.20.1


^ permalink raw reply related

* [PATCH v2 02/28] thunderbolt: Do not allocate switch if depth is greater than 6
From: Mika Westerberg @ 2019-02-06 13:17 UTC (permalink / raw)
  To: linux-kernel
  Cc: Michael Jamet, Yehezkel Bernat, Andreas Noever, Lukas Wunner,
	David S . Miller, Mika Westerberg, Andy Shevchenko, netdev
In-Reply-To: <20190206131738.43696-1-mika.westerberg@linux.intel.com>

Maximum depth in Thunderbolt topology is 6 so make sure it is not
possible to allocate switches that exceed the depth limit.

While at it update tb_switch_alloc() to use upper/lower_32_bits()
following tb_switch_alloc_safe_mode().

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/icm.c    |  5 ++---
 drivers/thunderbolt/switch.c | 18 ++++++++++++------
 drivers/thunderbolt/tb.h     |  1 +
 3 files changed, 15 insertions(+), 9 deletions(-)

diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c
index e3fc920af682..041e7ab0efd3 100644
--- a/drivers/thunderbolt/icm.c
+++ b/drivers/thunderbolt/icm.c
@@ -42,7 +42,6 @@
 #define ICM_TIMEOUT			5000	/* ms */
 #define ICM_APPROVE_TIMEOUT		10000	/* ms */
 #define ICM_MAX_LINK			4
-#define ICM_MAX_DEPTH			6
 
 /**
  * struct icm - Internal connection manager private data
@@ -709,7 +708,7 @@ icm_fr_device_disconnected(struct tb *tb, const struct icm_pkg_header *hdr)
 	depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >>
 		ICM_LINK_INFO_DEPTH_SHIFT;
 
-	if (link > ICM_MAX_LINK || depth > ICM_MAX_DEPTH) {
+	if (link > ICM_MAX_LINK || depth > TB_SWITCH_MAX_DEPTH) {
 		tb_warn(tb, "invalid topology %u.%u, ignoring\n", link, depth);
 		return;
 	}
@@ -739,7 +738,7 @@ icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr)
 	depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >>
 		ICM_LINK_INFO_DEPTH_SHIFT;
 
-	if (link > ICM_MAX_LINK || depth > ICM_MAX_DEPTH) {
+	if (link > ICM_MAX_LINK || depth > TB_SWITCH_MAX_DEPTH) {
 		tb_warn(tb, "invalid topology %u.%u, ignoring\n", link, depth);
 		return;
 	}
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index cd96994dc094..a90d21abed88 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -1155,10 +1155,16 @@ static int tb_switch_get_generation(struct tb_switch *sw)
 struct tb_switch *tb_switch_alloc(struct tb *tb, struct device *parent,
 				  u64 route)
 {
-	int i;
-	int cap;
 	struct tb_switch *sw;
-	int upstream_port = tb_cfg_get_upstream_port(tb->ctl, route);
+	int upstream_port;
+	int i, cap, depth;
+
+	/* Make sure we do not exceed maximum topology limit */
+	depth = tb_route_length(route);
+	if (depth > TB_SWITCH_MAX_DEPTH)
+		return NULL;
+
+	upstream_port = tb_cfg_get_upstream_port(tb->ctl, route);
 	if (upstream_port < 0)
 		return NULL;
 
@@ -1175,9 +1181,9 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, struct device *parent,
 
 	/* configure switch */
 	sw->config.upstream_port_number = upstream_port;
-	sw->config.depth = tb_route_length(route);
-	sw->config.route_lo = route;
-	sw->config.route_hi = route >> 32;
+	sw->config.depth = depth;
+	sw->config.route_hi = upper_32_bits(route);
+	sw->config.route_lo = lower_32_bits(route);
 	sw->config.enabled = 0;
 
 	/* initialize ports */
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 52584c4003e3..5faec5a8eb98 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -43,6 +43,7 @@ struct tb_switch_nvm {
 };
 
 #define TB_SWITCH_KEY_SIZE		32
+#define TB_SWITCH_MAX_DEPTH		6
 
 /**
  * struct tb_switch - a thunderbolt switch
-- 
2.20.1


^ permalink raw reply related

* [PATCH v2 14/28] thunderbolt: Extend tunnel creation to more than 2 adjacent switches
From: Mika Westerberg @ 2019-02-06 13:17 UTC (permalink / raw)
  To: linux-kernel
  Cc: Michael Jamet, Yehezkel Bernat, Andreas Noever, Lukas Wunner,
	David S . Miller, Mika Westerberg, Andy Shevchenko, netdev
In-Reply-To: <20190206131738.43696-1-mika.westerberg@linux.intel.com>

Now that we can allocate hop IDs per port on a path, we can take
advantage of this and create tunnels covering longer paths than just
between two adjacent switches. PCIe actually does not need this as it is
always a daisy chain between two adjacent switches but this way we do
not need to hard-code creation of the tunnel.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/path.c   | 94 ++++++++++++++++++++++++++++++++++--
 drivers/thunderbolt/tb.h     |  4 +-
 drivers/thunderbolt/tunnel.c | 54 +++++----------------
 3 files changed, 106 insertions(+), 46 deletions(-)

diff --git a/drivers/thunderbolt/path.c b/drivers/thunderbolt/path.c
index 48cb15ff4446..122e6a1daf34 100644
--- a/drivers/thunderbolt/path.c
+++ b/drivers/thunderbolt/path.c
@@ -31,23 +31,97 @@ static void tb_dump_hop(struct tb_port *port, struct tb_regs_hop *hop)
 }
 
 /**
- * tb_path_alloc() - allocate a thunderbolt path
+ * tb_path_alloc() - allocate a thunderbolt path between two ports
+ * @tb: Domain pointer
+ * @src: Source port of the path
+ * @dst: Destination port of the path
+ * @start_hopid: Hop ID used for the first ingress port in the path
+ * @end_hopid: Hop ID used for the last egress port in the path (%-1 for
+ *	       automatic allocation)
+ * @link_nr: Preferred link if there are dual links on the path
+ *
+ * Creates path between two ports starting with given @start_hopid. Reserves
+ * hop IDs for each port (they can be different from @start_hopid depending on
+ * how many hop IDs each port already have reserved). If there are dual
+ * links on the path, prioritizes using @link_nr.
  *
  * Return: Returns a tb_path on success or NULL on failure.
  */
-struct tb_path *tb_path_alloc(struct tb *tb, int num_hops)
+struct tb_path *tb_path_alloc(struct tb *tb, struct tb_port *src,
+			      struct tb_port *dst, int start_hopid,
+			      int end_hopid, int link_nr)
 {
-	struct tb_path *path = kzalloc(sizeof(*path), GFP_KERNEL);
+	struct tb_port *in_port, *out_port;
+	int in_hopid, out_hopid;
+	struct tb_path *path;
+	size_t num_hops;
+	int i, ret;
+
+	path = kzalloc(sizeof(*path), GFP_KERNEL);
 	if (!path)
 		return NULL;
+
+	i = 0;
+	tb_for_each_port(in_port, src, dst)
+		i++;
+
+	/* Each hop takes two ports */
+	num_hops = i / 2;
+
 	path->hops = kcalloc(num_hops, sizeof(*path->hops), GFP_KERNEL);
 	if (!path->hops) {
 		kfree(path);
 		return NULL;
 	}
+
+	in_hopid = start_hopid;
+	out_port = NULL;
+	out_hopid = -1;
+
+	for (i = 0; i < num_hops; i++) {
+		in_port = tb_port_get_next(src, dst, out_port);
+
+		if (in_port->dual_link_port && in_port->link_nr != link_nr)
+			in_port = in_port->dual_link_port;
+
+		ret = tb_port_alloc_in_hopid(in_port, in_hopid, -1);
+		if (ret < 0)
+			goto err;
+		in_hopid = ret;
+
+		out_port = tb_port_get_next(src, dst, in_port);
+		if (!out_port)
+			goto err;
+
+		if (out_port->dual_link_port && out_port->link_nr != link_nr)
+			out_port = out_port->dual_link_port;
+
+		if (end_hopid && i == num_hops - 1)
+			ret = tb_port_alloc_out_hopid(out_port, end_hopid,
+						      end_hopid);
+		else
+			ret = tb_port_alloc_out_hopid(out_port, -1, -1);
+
+		if (ret < 0)
+			goto err;
+		out_hopid = ret;
+
+		path->hops[i].in_hop_index = in_hopid;
+		path->hops[i].in_port = in_port;
+		path->hops[i].in_counter_index = -1;
+		path->hops[i].out_port = out_port;
+		path->hops[i].next_hop_index = out_hopid;
+
+		in_hopid = out_hopid;
+	}
+
 	path->tb = tb;
 	path->path_length = num_hops;
 	return path;
+
+err:
+	tb_path_free(path);
+	return NULL;
 }
 
 /**
@@ -55,10 +129,24 @@ struct tb_path *tb_path_alloc(struct tb *tb, int num_hops)
  */
 void tb_path_free(struct tb_path *path)
 {
+	int i;
+
 	if (path->activated) {
 		tb_WARN(path->tb, "trying to free an activated path\n")
 		return;
 	}
+
+	for (i = 0; i < path->path_length; i++) {
+		const struct tb_path_hop *hop = &path->hops[i];
+
+		if (hop->in_port)
+			tb_port_release_in_hopid(hop->in_port,
+						 hop->in_hop_index);
+		if (hop->out_port)
+			tb_port_release_out_hopid(hop->out_port,
+						  hop->next_hop_index);
+	}
+
 	kfree(path->hops);
 	kfree(path);
 }
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 683725915ff7..0e4d9088faf6 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -459,7 +459,9 @@ int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap);
 
 int tb_pci_port_enable(struct tb_port *port, bool enable);
 
-struct tb_path *tb_path_alloc(struct tb *tb, int num_hops);
+struct tb_path *tb_path_alloc(struct tb *tb, struct tb_port *src,
+			      struct tb_port *dst, int start_hopid,
+			      int end_hopid, int link_nr);
 void tb_path_free(struct tb_path *path);
 int tb_path_activate(struct tb_path *path);
 void tb_path_deactivate(struct tb_path *path);
diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c
index 20ce28276f7a..cdf9ca1c043e 100644
--- a/drivers/thunderbolt/tunnel.c
+++ b/drivers/thunderbolt/tunnel.c
@@ -12,6 +12,9 @@
 #include "tunnel.h"
 #include "tb.h"
 
+/* PCIe adapters use always hop ID of 8 for both directions */
+#define TB_PCI_HOPID			8
+
 #define TB_PCI_PATH_DOWN		0
 #define TB_PCI_PATH_UP			1
 
@@ -86,21 +89,13 @@ static void tb_pci_init_path(struct tb_path *path)
  * Allocate a PCI tunnel. The ports must be of type TB_TYPE_PCIE_UP and
  * TB_TYPE_PCIE_DOWN.
  *
- * Currently only paths consisting of two hops are supported (that is the
- * ports must be on "adjacent" switches).
- *
- * The paths are hard-coded to use hop 8 (the only working hop id available on
- * my thunderbolt devices). Therefore at most ONE path per device may be
- * activated.
- *
  * Return: Returns a tb_tunnel on success or NULL on failure.
  */
 struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up,
 				      struct tb_port *down)
 {
-	struct tb_path *path_to_up;
-	struct tb_path *path_to_down;
 	struct tb_tunnel *tunnel;
+	struct tb_path *path;
 
 	tunnel = tb_tunnel_alloc(tb, 2);
 	if (!tunnel)
@@ -110,46 +105,21 @@ struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up,
 	tunnel->src_port = down;
 	tunnel->dst_port = up;
 
-	path_to_up = tb_path_alloc(tb, 2);
-	if (!path_to_up) {
+	path = tb_path_alloc(tb, down, up, TB_PCI_HOPID, -1, 0);
+	if (!path) {
 		tb_tunnel_free(tunnel);
 		return NULL;
 	}
-	tunnel->paths[TB_PCI_PATH_UP] = path_to_up;
+	tb_pci_init_path(path);
+	tunnel->paths[TB_PCI_PATH_UP] = path;
 
-	path_to_down = tb_path_alloc(tb, 2);
-	if (!path_to_down) {
+	path = tb_path_alloc(tb, up, down, TB_PCI_HOPID, -1, 0);
+	if (!path) {
 		tb_tunnel_free(tunnel);
 		return NULL;
 	}
-	tunnel->paths[TB_PCI_PATH_DOWN] = path_to_down;
-
-	tb_pci_init_path(path_to_up);
-	tb_pci_init_path(path_to_down);
-
-	path_to_up->hops[0].in_port = down;
-	path_to_up->hops[0].in_hop_index = 8;
-	path_to_up->hops[0].in_counter_index = -1;
-	path_to_up->hops[0].out_port = tb_upstream_port(up->sw)->remote;
-	path_to_up->hops[0].next_hop_index = 8;
-
-	path_to_up->hops[1].in_port = tb_upstream_port(up->sw);
-	path_to_up->hops[1].in_hop_index = 8;
-	path_to_up->hops[1].in_counter_index = -1;
-	path_to_up->hops[1].out_port = up;
-	path_to_up->hops[1].next_hop_index = 8;
-
-	path_to_down->hops[0].in_port = up;
-	path_to_down->hops[0].in_hop_index = 8;
-	path_to_down->hops[0].in_counter_index = -1;
-	path_to_down->hops[0].out_port = tb_upstream_port(up->sw);
-	path_to_down->hops[0].next_hop_index = 8;
-
-	path_to_down->hops[1].in_port = tb_upstream_port(up->sw)->remote;
-	path_to_down->hops[1].in_hop_index = 8;
-	path_to_down->hops[1].in_counter_index = -1;
-	path_to_down->hops[1].out_port = down;
-	path_to_down->hops[1].next_hop_index = 8;
+	tb_pci_init_path(path);
+	tunnel->paths[TB_PCI_PATH_DOWN] = path;
 
 	return tunnel;
 }
-- 
2.20.1


^ permalink raw reply related

* [PATCH v2 11/28] thunderbolt: Generalize tunnel creation functionality
From: Mika Westerberg @ 2019-02-06 13:17 UTC (permalink / raw)
  To: linux-kernel
  Cc: Michael Jamet, Yehezkel Bernat, Andreas Noever, Lukas Wunner,
	David S . Miller, Mika Westerberg, Andy Shevchenko, netdev
In-Reply-To: <20190206131738.43696-1-mika.westerberg@linux.intel.com>

To be able to tunnel non-PCIe traffic, separate tunnel functionality
into generic and PCIe specific parts. Rename struct tb_pci_tunnel to
tb_tunnel, and make it hold an array of paths instead of just two.
Update all the tunneling functions to take this structure as parameter.

We also move tb_pci_port_active() to switch.c (and rename it) where we
will be keeping all port and switch related functions.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/switch.c  |  13 ++
 drivers/thunderbolt/tb.c      |  30 ++--
 drivers/thunderbolt/tb.h      |   2 +
 drivers/thunderbolt/tb_regs.h |   4 +
 drivers/thunderbolt/tunnel.c  | 298 ++++++++++++++++++++--------------
 drivers/thunderbolt/tunnel.h  |  38 +++--
 6 files changed, 235 insertions(+), 150 deletions(-)

diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index ec3c274ff278..b20af050ce9a 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -606,6 +606,19 @@ static int tb_init_port(struct tb_port *port)
 
 }
 
+/**
+ * tb_pci_port_enable() - Enable PCIe adapter port
+ * @port: PCIe port to enable
+ * @enable: Enable/disable the PCIe adapter
+ */
+int tb_pci_port_enable(struct tb_port *port, bool enable)
+{
+	u32 word = enable ? TB_PCI_EN : 0x0;
+	if (!port->cap_adap)
+		return -ENXIO;
+	return tb_port_write(port, &word, TB_CFG_PORT, port->cap_adap, 1);
+}
+
 /* switch utility functions */
 
 static void tb_dump_switch(struct tb *tb, struct tb_regs_switch_header *sw)
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index 931612143896..99f1c7e28d12 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -91,14 +91,14 @@ static void tb_scan_port(struct tb_port *port)
 static void tb_free_invalid_tunnels(struct tb *tb)
 {
 	struct tb_cm *tcm = tb_priv(tb);
-	struct tb_pci_tunnel *tunnel;
-	struct tb_pci_tunnel *n;
+	struct tb_tunnel *tunnel;
+	struct tb_tunnel *n;
 
 	list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) {
-		if (tb_pci_is_invalid(tunnel)) {
-			tb_pci_deactivate(tunnel);
+		if (tb_tunnel_is_invalid(tunnel)) {
+			tb_tunnel_deactivate(tunnel);
 			list_del(&tunnel->list);
-			tb_pci_free(tunnel);
+			tb_tunnel_free(tunnel);
 		}
 	}
 }
@@ -178,7 +178,7 @@ static void tb_activate_pcie_devices(struct tb *tb)
 	struct tb_switch *sw;
 	struct tb_port *up_port;
 	struct tb_port *down_port;
-	struct tb_pci_tunnel *tunnel;
+	struct tb_tunnel *tunnel;
 	struct tb_cm *tcm = tb_priv(tb);
 
 	/* scan for pcie devices at depth 1*/
@@ -214,17 +214,17 @@ static void tb_activate_pcie_devices(struct tb *tb)
 				     "All PCIe down ports are occupied, aborting\n");
 			continue;
 		}
-		tunnel = tb_pci_alloc(tb, up_port, down_port);
+		tunnel = tb_tunnel_alloc_pci(tb, up_port, down_port);
 		if (!tunnel) {
 			tb_port_info(up_port,
 				     "PCIe tunnel allocation failed, aborting\n");
 			continue;
 		}
 
-		if (tb_pci_activate(tunnel)) {
+		if (tb_tunnel_activate(tunnel)) {
 			tb_port_info(up_port,
 				     "PCIe tunnel activation failed, aborting\n");
-			tb_pci_free(tunnel);
+			tb_tunnel_free(tunnel);
 			continue;
 		}
 
@@ -350,13 +350,13 @@ static void tb_handle_event(struct tb *tb, enum tb_cfg_pkg_type type,
 static void tb_stop(struct tb *tb)
 {
 	struct tb_cm *tcm = tb_priv(tb);
-	struct tb_pci_tunnel *tunnel;
-	struct tb_pci_tunnel *n;
+	struct tb_tunnel *tunnel;
+	struct tb_tunnel *n;
 
 	/* tunnels are only present after everything has been initialized */
 	list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) {
-		tb_pci_deactivate(tunnel);
-		tb_pci_free(tunnel);
+		tb_tunnel_deactivate(tunnel);
+		tb_tunnel_free(tunnel);
 	}
 	tb_switch_remove(tb->root_switch);
 	tcm->hotplug_active = false; /* signal tb_handle_hotplug to quit */
@@ -415,7 +415,7 @@ static int tb_suspend_noirq(struct tb *tb)
 static int tb_resume_noirq(struct tb *tb)
 {
 	struct tb_cm *tcm = tb_priv(tb);
-	struct tb_pci_tunnel *tunnel, *n;
+	struct tb_tunnel *tunnel, *n;
 
 	tb_dbg(tb, "resuming...\n");
 
@@ -426,7 +426,7 @@ static int tb_resume_noirq(struct tb *tb)
 	tb_free_invalid_tunnels(tb);
 	tb_free_unplugged_children(tb->root_switch);
 	list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list)
-		tb_pci_restart(tunnel);
+		tb_tunnel_restart(tunnel);
 	if (!list_empty(&tcm->tunnel_list)) {
 		/*
 		 * the pcie links need some time to get going.
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index bab451ab31ff..a13d1cd53bc3 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -443,6 +443,8 @@ int tb_port_clear_counter(struct tb_port *port, int counter);
 int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec);
 int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap);
 
+int tb_pci_port_enable(struct tb_port *port, bool enable);
+
 struct tb_path *tb_path_alloc(struct tb *tb, int num_hops);
 void tb_path_free(struct tb_path *path);
 int tb_path_activate(struct tb_path *path);
diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h
index 82ac4ec8757f..75e935acade5 100644
--- a/drivers/thunderbolt/tb_regs.h
+++ b/drivers/thunderbolt/tb_regs.h
@@ -211,6 +211,10 @@ struct tb_regs_port_header {
 
 } __packed;
 
+/* PCIe adapter registers */
+
+#define TB_PCI_EN			BIT(31)
+
 /* Hop register from TB_CFG_HOPS. 8 byte per entry. */
 struct tb_regs_hop {
 	/* DWORD 0 */
diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c
index 1e470564e99d..20ce28276f7a 100644
--- a/drivers/thunderbolt/tunnel.c
+++ b/drivers/thunderbolt/tunnel.c
@@ -1,8 +1,9 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * Thunderbolt Cactus Ridge driver - Tunneling support
+ * Thunderbolt driver - Tunneling support
  *
  * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com>
+ * Copyright (C) 2019, Intel Corporation
  */
 
 #include <linux/slab.h>
@@ -11,14 +12,17 @@
 #include "tunnel.h"
 #include "tb.h"
 
+#define TB_PCI_PATH_DOWN		0
+#define TB_PCI_PATH_UP			1
+
 #define __TB_TUNNEL_PRINT(level, tunnel, fmt, arg...)                   \
 	do {                                                            \
-		struct tb_pci_tunnel *__tunnel = (tunnel);              \
+		struct tb_tunnel *__tunnel = (tunnel);                  \
 		level(__tunnel->tb, "%llx:%x <-> %llx:%x (PCI): " fmt,  \
-		      tb_route(__tunnel->down_port->sw),                \
-		      __tunnel->down_port->port,                        \
-		      tb_route(__tunnel->up_port->sw),                  \
-		      __tunnel->up_port->port,                          \
+		      tb_route(__tunnel->src_port->sw),                 \
+		      __tunnel->src_port->port,                         \
+		      tb_route(__tunnel->dst_port->sw),                 \
+		      __tunnel->dst_port->port,                         \
 		      ## arg);                                          \
 	} while (0)
 
@@ -29,6 +33,38 @@
 #define tb_tunnel_info(tunnel, fmt, arg...) \
 	__TB_TUNNEL_PRINT(tb_info, tunnel, fmt, ##arg)
 
+static struct tb_tunnel *tb_tunnel_alloc(struct tb *tb, size_t npaths)
+{
+	struct tb_tunnel *tunnel;
+
+	tunnel = kzalloc(sizeof(*tunnel), GFP_KERNEL);
+	if (!tunnel)
+		return NULL;
+
+	tunnel->paths = kcalloc(npaths, sizeof(tunnel->paths[0]), GFP_KERNEL);
+	if (!tunnel->paths) {
+		tb_tunnel_free(tunnel);
+		return NULL;
+	}
+
+	INIT_LIST_HEAD(&tunnel->list);
+	tunnel->tb = tb;
+	tunnel->npaths = npaths;
+
+	return tunnel;
+}
+
+static int tb_pci_activate(struct tb_tunnel *tunnel, bool activate)
+{
+	int res;
+
+	res = tb_pci_port_enable(tunnel->src_port, activate);
+	if (res)
+		return res;
+
+	return tb_pci_port_enable(tunnel->dst_port, activate);
+}
+
 static void tb_pci_init_path(struct tb_path *path)
 {
 	path->egress_fc_enable = TB_PATH_SOURCE | TB_PATH_INTERNAL;
@@ -42,7 +78,10 @@ static void tb_pci_init_path(struct tb_path *path)
 }
 
 /**
- * tb_pci_alloc() - allocate a pci tunnel
+ * tb_tunnel_alloc_pci() - allocate a pci tunnel
+ * @tb: Pointer to the domain structure
+ * @up: PCIe upstream adapter port
+ * @down: PCIe downstream adapter port
  *
  * Allocate a PCI tunnel. The ports must be of type TB_TYPE_PCIE_UP and
  * TB_TYPE_PCIE_DOWN.
@@ -54,170 +93,185 @@ static void tb_pci_init_path(struct tb_path *path)
  * my thunderbolt devices). Therefore at most ONE path per device may be
  * activated.
  *
- * Return: Returns a tb_pci_tunnel on success or NULL on failure.
+ * Return: Returns a tb_tunnel on success or NULL on failure.
  */
-struct tb_pci_tunnel *tb_pci_alloc(struct tb *tb, struct tb_port *up,
-				   struct tb_port *down)
+struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up,
+				      struct tb_port *down)
 {
-	struct tb_pci_tunnel *tunnel = kzalloc(sizeof(*tunnel), GFP_KERNEL);
+	struct tb_path *path_to_up;
+	struct tb_path *path_to_down;
+	struct tb_tunnel *tunnel;
+
+	tunnel = tb_tunnel_alloc(tb, 2);
 	if (!tunnel)
-		goto err;
-	tunnel->tb = tb;
-	tunnel->down_port = down;
-	tunnel->up_port = up;
-	INIT_LIST_HEAD(&tunnel->list);
-	tunnel->path_to_up = tb_path_alloc(up->sw->tb, 2);
-	if (!tunnel->path_to_up)
-		goto err;
-	tunnel->path_to_down = tb_path_alloc(up->sw->tb, 2);
-	if (!tunnel->path_to_down)
-		goto err;
-	tb_pci_init_path(tunnel->path_to_up);
-	tb_pci_init_path(tunnel->path_to_down);
-
-	tunnel->path_to_up->hops[0].in_port = down;
-	tunnel->path_to_up->hops[0].in_hop_index = 8;
-	tunnel->path_to_up->hops[0].in_counter_index = -1;
-	tunnel->path_to_up->hops[0].out_port = tb_upstream_port(up->sw)->remote;
-	tunnel->path_to_up->hops[0].next_hop_index = 8;
-
-	tunnel->path_to_up->hops[1].in_port = tb_upstream_port(up->sw);
-	tunnel->path_to_up->hops[1].in_hop_index = 8;
-	tunnel->path_to_up->hops[1].in_counter_index = -1;
-	tunnel->path_to_up->hops[1].out_port = up;
-	tunnel->path_to_up->hops[1].next_hop_index = 8;
-
-	tunnel->path_to_down->hops[0].in_port = up;
-	tunnel->path_to_down->hops[0].in_hop_index = 8;
-	tunnel->path_to_down->hops[0].in_counter_index = -1;
-	tunnel->path_to_down->hops[0].out_port = tb_upstream_port(up->sw);
-	tunnel->path_to_down->hops[0].next_hop_index = 8;
-
-	tunnel->path_to_down->hops[1].in_port =
-		tb_upstream_port(up->sw)->remote;
-	tunnel->path_to_down->hops[1].in_hop_index = 8;
-	tunnel->path_to_down->hops[1].in_counter_index = -1;
-	tunnel->path_to_down->hops[1].out_port = down;
-	tunnel->path_to_down->hops[1].next_hop_index = 8;
-	return tunnel;
+		return NULL;
 
-err:
-	if (tunnel) {
-		if (tunnel->path_to_down)
-			tb_path_free(tunnel->path_to_down);
-		if (tunnel->path_to_up)
-			tb_path_free(tunnel->path_to_up);
-		kfree(tunnel);
+	tunnel->activate = tb_pci_activate;
+	tunnel->src_port = down;
+	tunnel->dst_port = up;
+
+	path_to_up = tb_path_alloc(tb, 2);
+	if (!path_to_up) {
+		tb_tunnel_free(tunnel);
+		return NULL;
 	}
-	return NULL;
+	tunnel->paths[TB_PCI_PATH_UP] = path_to_up;
+
+	path_to_down = tb_path_alloc(tb, 2);
+	if (!path_to_down) {
+		tb_tunnel_free(tunnel);
+		return NULL;
+	}
+	tunnel->paths[TB_PCI_PATH_DOWN] = path_to_down;
+
+	tb_pci_init_path(path_to_up);
+	tb_pci_init_path(path_to_down);
+
+	path_to_up->hops[0].in_port = down;
+	path_to_up->hops[0].in_hop_index = 8;
+	path_to_up->hops[0].in_counter_index = -1;
+	path_to_up->hops[0].out_port = tb_upstream_port(up->sw)->remote;
+	path_to_up->hops[0].next_hop_index = 8;
+
+	path_to_up->hops[1].in_port = tb_upstream_port(up->sw);
+	path_to_up->hops[1].in_hop_index = 8;
+	path_to_up->hops[1].in_counter_index = -1;
+	path_to_up->hops[1].out_port = up;
+	path_to_up->hops[1].next_hop_index = 8;
+
+	path_to_down->hops[0].in_port = up;
+	path_to_down->hops[0].in_hop_index = 8;
+	path_to_down->hops[0].in_counter_index = -1;
+	path_to_down->hops[0].out_port = tb_upstream_port(up->sw);
+	path_to_down->hops[0].next_hop_index = 8;
+
+	path_to_down->hops[1].in_port = tb_upstream_port(up->sw)->remote;
+	path_to_down->hops[1].in_hop_index = 8;
+	path_to_down->hops[1].in_counter_index = -1;
+	path_to_down->hops[1].out_port = down;
+	path_to_down->hops[1].next_hop_index = 8;
+
+	return tunnel;
 }
 
 /**
- * tb_pci_free() - free a tunnel
+ * tb_tunnel_free() - free a tunnel
+ * @tunnel: Tunnel to be freed
  *
  * The tunnel must have been deactivated.
  */
-void tb_pci_free(struct tb_pci_tunnel *tunnel)
+void tb_tunnel_free(struct tb_tunnel *tunnel)
 {
-	if (tunnel->path_to_up->activated || tunnel->path_to_down->activated) {
-		tb_tunnel_WARN(tunnel, "trying to free an activated tunnel\n");
+	int i;
+
+	if (!tunnel)
 		return;
+
+	for (i = 0; i < tunnel->npaths; i++) {
+		if (tunnel->paths[i] && tunnel->paths[i]->activated) {
+			tb_tunnel_WARN(tunnel,
+				       "trying to free an activated tunnel\n");
+			return;
+		}
 	}
-	tb_path_free(tunnel->path_to_up);
-	tb_path_free(tunnel->path_to_down);
+
+	for (i = 0; i < tunnel->npaths; i++) {
+		if (tunnel->paths[i])
+			tb_path_free(tunnel->paths[i]);
+	}
+
+	kfree(tunnel->paths);
 	kfree(tunnel);
 }
 
 /**
- * tb_pci_is_invalid - check whether an activated path is still valid
+ * tb_tunnel_is_invalid - check whether an activated path is still valid
+ * @tunnel: Tunnel to check
  */
-bool tb_pci_is_invalid(struct tb_pci_tunnel *tunnel)
+bool tb_tunnel_is_invalid(struct tb_tunnel *tunnel)
 {
-	WARN_ON(!tunnel->path_to_up->activated);
-	WARN_ON(!tunnel->path_to_down->activated);
+	int i;
 
-	return tb_path_is_invalid(tunnel->path_to_up)
-	       || tb_path_is_invalid(tunnel->path_to_down);
-}
+	for (i = 0; i < tunnel->npaths; i++) {
+		WARN_ON(!tunnel->paths[i]->activated);
+		if (tb_path_is_invalid(tunnel->paths[i]))
+			return true;
+	}
 
-/**
- * tb_pci_port_active() - activate/deactivate PCI capability
- *
- * Return: Returns 0 on success or an error code on failure.
- */
-static int tb_pci_port_active(struct tb_port *port, bool active)
-{
-	u32 word = active ? 0x80000000 : 0x0;
-	if (!port->cap_adap)
-		return -ENXIO;
-	return tb_port_write(port, &word, TB_CFG_PORT, port->cap_adap, 1);
+	return false;
 }
 
 /**
- * tb_pci_restart() - activate a tunnel after a hardware reset
+ * tb_tunnel_restart() - activate a tunnel after a hardware reset
+ * @tunnel: Tunnel to restart
+ *
+ * Return: 0 on success and negative errno in case if failure
  */
-int tb_pci_restart(struct tb_pci_tunnel *tunnel)
+int tb_tunnel_restart(struct tb_tunnel *tunnel)
 {
-	int res;
-	tunnel->path_to_up->activated = false;
-	tunnel->path_to_down->activated = false;
+	int res, i;
 
 	tb_tunnel_info(tunnel, "activating\n");
 
-	res = tb_path_activate(tunnel->path_to_up);
-	if (res)
-		goto err;
-	res = tb_path_activate(tunnel->path_to_down);
-	if (res)
-		goto err;
+	for (i = 0; i < tunnel->npaths; i++) {
+		tunnel->paths[i]->activated = false;
+		res = tb_path_activate(tunnel->paths[i]);
+		if (res)
+			goto err;
+	}
 
-	res = tb_pci_port_active(tunnel->down_port, true);
-	if (res)
-		goto err;
+	if (tunnel->activate) {
+		res = tunnel->activate(tunnel, true);
+		if (res)
+			goto err;
+	}
 
-	res = tb_pci_port_active(tunnel->up_port, true);
-	if (res)
-		goto err;
 	return 0;
+
 err:
 	tb_tunnel_warn(tunnel, "activation failed\n");
-	tb_pci_deactivate(tunnel);
+	tb_tunnel_deactivate(tunnel);
 	return res;
 }
 
 /**
- * tb_pci_activate() - activate a tunnel
+ * tb_tunnel_activate() - activate a tunnel
+ * @tunnel: Tunnel to activate
  *
  * Return: Returns 0 on success or an error code on failure.
  */
-int tb_pci_activate(struct tb_pci_tunnel *tunnel)
+int tb_tunnel_activate(struct tb_tunnel *tunnel)
 {
-	if (tunnel->path_to_up->activated || tunnel->path_to_down->activated) {
-		tb_tunnel_WARN(tunnel,
-			       "trying to activate an already activated tunnel\n");
-		return -EINVAL;
-	}
+	int i;
 
-	return tb_pci_restart(tunnel);
-}
+	tb_tunnel_info(tunnel, "activating\n");
 
+	for (i = 0; i < tunnel->npaths; i++) {
+		if (tunnel->paths[i]->activated) {
+			tb_tunnel_WARN(tunnel,
+				       "trying to activate an already activated tunnel\n");
+			return -EINVAL;
+		}
+	}
 
+	return tb_tunnel_restart(tunnel);
+}
 
 /**
- * tb_pci_deactivate() - deactivate a tunnel
+ * tb_tunnel_deactivate() - deactivate a tunnel
+ * @tunnel: Tunnel to deactivate
  */
-void tb_pci_deactivate(struct tb_pci_tunnel *tunnel)
+void tb_tunnel_deactivate(struct tb_tunnel *tunnel)
 {
+	int i;
+
 	tb_tunnel_info(tunnel, "deactivating\n");
-	/*
-	 * TODO: enable reset by writing 0x04000000 to TB_CAP_PCIE + 1 on up
-	 * port. Seems to have no effect?
-	 */
-	tb_pci_port_active(tunnel->up_port, false);
-	tb_pci_port_active(tunnel->down_port, false);
-	if (tunnel->path_to_down->activated)
-		tb_path_deactivate(tunnel->path_to_down);
-	if (tunnel->path_to_up->activated)
-		tb_path_deactivate(tunnel->path_to_up);
-}
 
+	if (tunnel->activate)
+		tunnel->activate(tunnel, false);
+
+	for (i = 0; i < tunnel->npaths; i++) {
+		if (tunnel->paths[i]->activated)
+			tb_path_deactivate(tunnel->paths[i]);
+	}
+}
diff --git a/drivers/thunderbolt/tunnel.h b/drivers/thunderbolt/tunnel.h
index dff0f27d6ab5..b4e992165e56 100644
--- a/drivers/thunderbolt/tunnel.h
+++ b/drivers/thunderbolt/tunnel.h
@@ -1,8 +1,9 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 /*
- * Thunderbolt Cactus Ridge driver - Tunneling support
+ * Thunderbolt driver - Tunneling support
  *
  * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com>
+ * Copyright (C) 2019, Intel Corporation
  */
 
 #ifndef TB_TUNNEL_H_
@@ -10,22 +11,33 @@
 
 #include "tb.h"
 
-struct tb_pci_tunnel {
+/**
+ * struct tb_tunnel - Tunnel between two ports
+ * @tb: Pointer to the domain
+ * @src_port: Source port of the tunnel
+ * @dst_port: Destination port of the tunnel
+ * @paths: All paths required by the tunnel
+ * @npaths: Number of paths in @paths
+ * @activate: Optional tunnel specific activation/deactivation
+ * @list: Tunnels are linked using this field
+ */
+struct tb_tunnel {
 	struct tb *tb;
-	struct tb_port *up_port;
-	struct tb_port *down_port;
-	struct tb_path *path_to_up;
-	struct tb_path *path_to_down;
+	struct tb_port *src_port;
+	struct tb_port *dst_port;
+	struct tb_path **paths;
+	size_t npaths;
+	int (*activate)(struct tb_tunnel *tunnel, bool activate);
 	struct list_head list;
 };
 
-struct tb_pci_tunnel *tb_pci_alloc(struct tb *tb, struct tb_port *up,
-				   struct tb_port *down);
-void tb_pci_free(struct tb_pci_tunnel *tunnel);
-int tb_pci_activate(struct tb_pci_tunnel *tunnel);
-int tb_pci_restart(struct tb_pci_tunnel *tunnel);
-void tb_pci_deactivate(struct tb_pci_tunnel *tunnel);
-bool tb_pci_is_invalid(struct tb_pci_tunnel *tunnel);
+struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up,
+				      struct tb_port *down);
+void tb_tunnel_free(struct tb_tunnel *tunnel);
+int tb_tunnel_activate(struct tb_tunnel *tunnel);
+int tb_tunnel_restart(struct tb_tunnel *tunnel);
+void tb_tunnel_deactivate(struct tb_tunnel *tunnel);
+bool tb_tunnel_is_invalid(struct tb_tunnel *tunnel);
 
 #endif
 
-- 
2.20.1


^ permalink raw reply related

* [PATCH v2 09/28] thunderbolt: Cache adapter specific capability offset into struct port
From: Mika Westerberg @ 2019-02-06 13:17 UTC (permalink / raw)
  To: linux-kernel
  Cc: Michael Jamet, Yehezkel Bernat, Andreas Noever, Lukas Wunner,
	David S . Miller, Mika Westerberg, Andy Shevchenko, netdev
In-Reply-To: <20190206131738.43696-1-mika.westerberg@linux.intel.com>

The adapter specific capability either is there or not if the port does
not hold an adapter. Instead of always finding it on-demand we read the
offset just once when the port is initialized.

While there we update the struct port documentation to follow kernel-doc
format.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/switch.c     | 4 ++++
 drivers/thunderbolt/tb.c         | 8 ++++----
 drivers/thunderbolt/tb.h         | 2 ++
 drivers/thunderbolt/tunnel_pci.c | 9 +++------
 4 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index 1eee2502b5ba..ec3c274ff278 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -593,6 +593,10 @@ static int tb_init_port(struct tb_port *port)
 			port->cap_phy = cap;
 		else
 			tb_port_WARN(port, "non switch port without a PHY\n");
+	} else if (port->port != 0) {
+		cap = tb_port_find_cap(port, TB_PORT_CAP_ADAP);
+		if (cap > 0)
+			port->cap_adap = cap;
 	}
 
 	tb_dump_port(port->sw->tb, &port->config);
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index 30e02c716f6c..7fd88b41d082 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -151,8 +151,8 @@ static struct tb_port *tb_find_unused_down_port(struct tb_switch *sw)
 			continue;
 		if (sw->ports[i].config.type != TB_TYPE_PCIE_DOWN)
 			continue;
-		cap = tb_port_find_cap(&sw->ports[i], TB_PORT_CAP_ADAP);
-		if (cap < 0)
+		cap = sw->ports[i].cap_adap;
+		if (!cap)
 			continue;
 		res = tb_port_read(&sw->ports[i], &data, TB_CFG_PORT, cap, 1);
 		if (res < 0)
@@ -197,8 +197,8 @@ static void tb_activate_pcie_devices(struct tb *tb)
 		}
 
 		/* check whether port is already activated */
-		cap = tb_port_find_cap(up_port, TB_PORT_CAP_ADAP);
-		if (cap < 0)
+		cap = up_port->cap_adap;
+		if (!cap)
 			continue;
 		if (tb_port_read(up_port, &data, TB_CFG_PORT, cap, 1))
 			continue;
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 3160169389cc..bab451ab31ff 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -124,6 +124,7 @@ struct tb_switch {
  * @remote: Remote port (%NULL if not connected)
  * @xdomain: Remote host (%NULL if not connected)
  * @cap_phy: Offset, zero if not found
+ * @cap_adap: Offset of the adapter specific capability (%0 if not present)
  * @port: Port number on switch
  * @disabled: Disabled by eeprom
  * @dual_link_port: If the switch is connected using two ports, points
@@ -136,6 +137,7 @@ struct tb_port {
 	struct tb_port *remote;
 	struct tb_xdomain *xdomain;
 	int cap_phy;
+	int cap_adap;
 	u8 port;
 	bool disabled;
 	struct tb_port *dual_link_port;
diff --git a/drivers/thunderbolt/tunnel_pci.c b/drivers/thunderbolt/tunnel_pci.c
index 0637537ea53f..2de4edccbd6d 100644
--- a/drivers/thunderbolt/tunnel_pci.c
+++ b/drivers/thunderbolt/tunnel_pci.c
@@ -148,12 +148,9 @@ bool tb_pci_is_invalid(struct tb_pci_tunnel *tunnel)
 static int tb_pci_port_active(struct tb_port *port, bool active)
 {
 	u32 word = active ? 0x80000000 : 0x0;
-	int cap = tb_port_find_cap(port, TB_PORT_CAP_ADAP);
-	if (cap < 0) {
-		tb_port_warn(port, "TB_PORT_CAP_ADAP not found: %d\n", cap);
-		return cap;
-	}
-	return tb_port_write(port, &word, TB_CFG_PORT, cap, 1);
+	if (!port->cap_adap)
+		return -ENXIO;
+	return tb_port_write(port, &word, TB_CFG_PORT, port->cap_adap, 1);
 }
 
 /**
-- 
2.20.1


^ permalink raw reply related

* [PATCH v2 13/28] thunderbolt: Add helper function to iterate from one port to another
From: Mika Westerberg @ 2019-02-06 13:17 UTC (permalink / raw)
  To: linux-kernel
  Cc: Michael Jamet, Yehezkel Bernat, Andreas Noever, Lukas Wunner,
	David S . Miller, Mika Westerberg, Andy Shevchenko, netdev
In-Reply-To: <20190206131738.43696-1-mika.westerberg@linux.intel.com>

We need to be able to walk from one port to another when we are creating
paths where there are multiple switches between two ports. For this
reason introduce a new function tb_port_get_next() and a new macro
tb_for_each_port().

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/switch.c | 60 ++++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/tb.h     |  6 ++++
 2 files changed, 66 insertions(+)

diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index 320f64ebe8b8..23b6bae8362e 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -683,6 +683,66 @@ void tb_port_release_out_hopid(struct tb_port *port, int hopid)
 	ida_simple_remove(&port->out_hopids, hopid);
 }
 
+/**
+ * tb_port_get_next() - Return next port for given port
+ * @start: Start port of the walk
+ * @end: End port of the walk
+ * @prev: Previous port (%NULL if this is the first)
+ *
+ * This function can be used to walk from one port to another if they
+ * are connected through zero or more switches. If the @prev is dual
+ * link port, the function follows that link and returns another end on
+ * that same link.
+ *
+ * If the walk cannot be continued, returns %NULL.
+ *
+ * Domain tb->lock must be held when this function is called.
+ */
+struct tb_port *tb_port_get_next(struct tb_port *start, struct tb_port *end,
+				 struct tb_port *prev)
+{
+	struct tb_port *port, *next;
+
+	if (!prev)
+		return start;
+
+	if (prev->sw == end->sw) {
+		if (prev != end)
+			return end;
+		return NULL;
+	}
+
+	/* Switch back to use primary links for walking */
+	if (prev->dual_link_port && prev->link_nr)
+		port = prev->dual_link_port;
+	else
+		port = prev;
+
+	if (start->sw->config.depth < end->sw->config.depth) {
+		if (port->remote &&
+		    port->remote->sw->config.depth > port->sw->config.depth)
+			next = port->remote;
+		else
+			next = tb_port_at(tb_route(end->sw), port->sw);
+	} else if (start->sw->config.depth > end->sw->config.depth) {
+		if (tb_is_upstream_port(port))
+			next = port->remote;
+		else
+			next = tb_upstream_port(port->sw);
+	} else {
+		/* Must be the same switch then */
+		if (start->sw != end->sw)
+			return NULL;
+		return end;
+	}
+
+	/* If prev was dual link return another end of that link then */
+	if (next->dual_link_port && next->link_nr != prev->link_nr)
+		return next->dual_link_port;
+
+	return next;
+}
+
 /**
  * tb_pci_port_enable() - Enable PCIe adapter port
  * @port: PCIe port to enable
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index bfa1cee193fd..683725915ff7 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -447,6 +447,12 @@ int tb_port_alloc_in_hopid(struct tb_port *port, int hopid, int max_hopid);
 void tb_port_release_in_hopid(struct tb_port *port, int hopid);
 int tb_port_alloc_out_hopid(struct tb_port *port, int hopid, int max_hopid);
 void tb_port_release_out_hopid(struct tb_port *port, int hopid);
+struct tb_port *tb_port_get_next(struct tb_port *start, struct tb_port *end,
+				 struct tb_port *prev);
+
+#define tb_for_each_port(port, start, end)			\
+	for (port = tb_port_get_next(start, end, NULL); port;	\
+	     port = tb_port_get_next(start, end, port))
 
 int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec);
 int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap);
-- 
2.20.1


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox