Commit 3d411378 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge tag 'thunderbolt-for-v5.11-rc1' of...

Merge tag 'thunderbolt-for-v5.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt into usb-next

Mika writes:

thunderbolt: Changes for v5.11 merge window

This includes following Thunderbolt/USB4 changes for v5.11 merge window:

  * DMA traffic test driver

  * USB4 router NVM upgrade improvements

  * USB4 router operations proxy implementation available in the recent
    Intel Connection Manager firmwares

  * Support for Intel Maple Ridge discrete Thunderbolt 4 controller

  * A couple of cleanups and minor improvements.

* tag 'thunderbolt-for-v5.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt: (22 commits)
  thunderbolt: Add support for Intel Maple Ridge
  thunderbolt: Add USB4 router operation proxy for firmware connection manager
  thunderbolt: Move constants for USB4 router operations to tb_regs.h
  thunderbolt: Add connection manager specific hooks for USB4 router operations
  thunderbolt: Pass TX and RX data directly to usb4_switch_op()
  thunderbolt: Pass metadata directly to usb4_switch_op()
  thunderbolt: Perform USB4 router NVM upgrade in two phases
  thunderbolt: Return -ENOTCONN when ERR_CONN is received
  thunderbolt: Keep the parent runtime resumed for a while on device disconnect
  thunderbolt: Log adapter numbers in decimal in path activation/deactivation
  thunderbolt: Log which connection manager implementation is used
  thunderbolt: Move max_boot_acl field to correct place in struct icm
  MAINTAINERS: Add Isaac as maintainer of Thunderbolt DMA traffic test driver
  thunderbolt: Add DMA traffic test driver
  thunderbolt: Add support for end-to-end flow control
  thunderbolt: Make it possible to allocate one directional DMA tunnel
  thunderbolt: Create debugfs directory automatically for services
  thunderbolt: Add functions for enabling and disabling lane bonding on XDomain
  thunderbolt: Add link_speed and link_width to XDomain
  thunderbolt: Create XDomain devices for loops back to the host
  ...
parents 08a02f95 db0746e3
What: /sys/bus/thunderbolt/devices/<xdomain>/rx_speed
Date: Feb 2021
KernelVersion: 5.11
Contact: Isaac Hazan <isaac.hazan@intel.com>
Description: This attribute reports the XDomain RX speed per lane.
All RX lanes run at the same speed.
What: /sys/bus/thunderbolt/devices/<xdomain>/rx_lanes
Date: Feb 2021
KernelVersion: 5.11
Contact: Isaac Hazan <isaac.hazan@intel.com>
Description: This attribute reports the number of RX lanes the XDomain
is using simultaneously through its upstream port.
What: /sys/bus/thunderbolt/devices/<xdomain>/tx_speed
Date: Feb 2021
KernelVersion: 5.11
Contact: Isaac Hazan <isaac.hazan@intel.com>
Description: This attribute reports the XDomain TX speed per lane.
All TX lanes run at the same speed.
What: /sys/bus/thunderbolt/devices/<xdomain>/tx_lanes
Date: Feb 2021
KernelVersion: 5.11
Contact: Isaac Hazan <isaac.hazan@intel.com>
Description: This attribute reports number of TX lanes the XDomain
is using simultaneously through its upstream port.
What: /sys/bus/thunderbolt/devices/.../domainX/boot_acl
Date: Jun 2018
KernelVersion: 4.17
......
......@@ -17372,6 +17372,12 @@ W: http://thinkwiki.org/wiki/Ibm-acpi
T: git git://repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git
F: drivers/platform/x86/thinkpad_acpi.c
THUNDERBOLT DMA TRAFFIC TEST DRIVER
M: Isaac Hazan <isaac.hazan@intel.com>
L: linux-usb@vger.kernel.org
S: Maintained
F: drivers/thunderbolt/dma_test.c
THUNDERBOLT DRIVER
M: Andreas Noever <andreas.noever@gmail.com>
M: Michael Jamet <michael.jamet@intel.com>
......
......@@ -866,7 +866,7 @@ static int tbnet_open(struct net_device *dev)
eof_mask = BIT(TBIP_PDF_FRAME_END);
ring = tb_ring_alloc_rx(xd->tb->nhi, -1, TBNET_RING_SIZE,
RING_FLAG_FRAME, sof_mask, eof_mask,
RING_FLAG_FRAME, 0, sof_mask, eof_mask,
tbnet_start_poll, net);
if (!ring) {
netdev_err(dev, "failed to allocate Rx ring\n");
......
......@@ -31,4 +31,17 @@ config USB4_KUNIT_TEST
bool "KUnit tests"
depends on KUNIT=y
config USB4_DMA_TEST
tristate "DMA traffic test driver"
depends on DEBUG_FS
help
This allows sending and receiving DMA traffic through loopback
connection. Loopback connection can be done by either special
dongle that has TX/RX lines crossed, or by simply connecting a
cable back to the host. Only enable this if you know what you
are doing. Normal users and distro kernels should say N here.
To compile this driver a module, choose M here. The module will be
called thunderbolt_dma_test.
endif # USB4
......@@ -7,3 +7,6 @@ thunderbolt-objs += nvm.o retimer.o quirks.o
thunderbolt-${CONFIG_ACPI} += acpi.o
thunderbolt-$(CONFIG_DEBUG_FS) += debugfs.o
thunderbolt-${CONFIG_USB4_KUNIT_TEST} += test.o
thunderbolt_dma_test-${CONFIG_USB4_DMA_TEST} += dma_test.o
obj-$(CONFIG_USB4_DMA_TEST) += thunderbolt_dma_test.o
......@@ -628,8 +628,8 @@ struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data)
if (!ctl->tx)
goto err;
ctl->rx = tb_ring_alloc_rx(nhi, 0, 10, RING_FLAG_NO_SUSPEND, 0xffff,
0xffff, NULL, NULL);
ctl->rx = tb_ring_alloc_rx(nhi, 0, 10, RING_FLAG_NO_SUSPEND, 0, 0xffff,
0xffff, NULL, NULL);
if (!ctl->rx)
goto err;
......@@ -962,6 +962,9 @@ static int tb_cfg_get_error(struct tb_ctl *ctl, enum tb_cfg_space space,
if (res->tb_error == TB_CFG_ERROR_LOCK)
return -EACCES;
else if (res->tb_error == TB_CFG_ERROR_PORT_NOT_CONNECTED)
return -ENOTCONN;
return -EIO;
}
......
......@@ -691,6 +691,30 @@ void tb_switch_debugfs_remove(struct tb_switch *sw)
debugfs_remove_recursive(sw->debugfs_dir);
}
/**
* tb_service_debugfs_init() - Add debugfs directory for service
* @svc: Thunderbolt service pointer
*
* Adds debugfs directory for service.
*/
void tb_service_debugfs_init(struct tb_service *svc)
{
svc->debugfs_dir = debugfs_create_dir(dev_name(&svc->dev),
tb_debugfs_root);
}
/**
* tb_service_debugfs_remove() - Remove service debugfs directory
* @svc: Thunderbolt service pointer
*
* Removes the previously created debugfs directory for @svc.
*/
void tb_service_debugfs_remove(struct tb_service *svc)
{
debugfs_remove_recursive(svc->debugfs_dir);
svc->debugfs_dir = NULL;
}
void tb_debugfs_init(void)
{
tb_debugfs_root = debugfs_create_dir("thunderbolt", NULL);
......
This diff is collapsed.
This diff is collapsed.
......@@ -494,7 +494,7 @@ static int nhi_alloc_hop(struct tb_nhi *nhi, struct tb_ring *ring)
static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size,
bool transmit, unsigned int flags,
u16 sof_mask, u16 eof_mask,
int e2e_tx_hop, u16 sof_mask, u16 eof_mask,
void (*start_poll)(void *),
void *poll_data)
{
......@@ -517,6 +517,7 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size,
ring->is_tx = transmit;
ring->size = size;
ring->flags = flags;
ring->e2e_tx_hop = e2e_tx_hop;
ring->sof_mask = sof_mask;
ring->eof_mask = eof_mask;
ring->head = 0;
......@@ -561,7 +562,7 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size,
struct tb_ring *tb_ring_alloc_tx(struct tb_nhi *nhi, int hop, int size,
unsigned int flags)
{
return tb_ring_alloc(nhi, hop, size, true, flags, 0, 0, NULL, NULL);
return tb_ring_alloc(nhi, hop, size, true, flags, 0, 0, 0, NULL, NULL);
}
EXPORT_SYMBOL_GPL(tb_ring_alloc_tx);
......@@ -571,6 +572,7 @@ EXPORT_SYMBOL_GPL(tb_ring_alloc_tx);
* @hop: HopID (ring) to allocate. Pass %-1 for automatic allocation.
* @size: Number of entries in the ring
* @flags: Flags for the ring
* @e2e_tx_hop: Transmit HopID when E2E is enabled in @flags
* @sof_mask: Mask of PDF values that start a frame
* @eof_mask: Mask of PDF values that end a frame
* @start_poll: If not %NULL the ring will call this function when an
......@@ -579,10 +581,11 @@ EXPORT_SYMBOL_GPL(tb_ring_alloc_tx);
* @poll_data: Optional data passed to @start_poll
*/
struct tb_ring *tb_ring_alloc_rx(struct tb_nhi *nhi, int hop, int size,
unsigned int flags, u16 sof_mask, u16 eof_mask,
unsigned int flags, int e2e_tx_hop,
u16 sof_mask, u16 eof_mask,
void (*start_poll)(void *), void *poll_data)
{
return tb_ring_alloc(nhi, hop, size, false, flags, sof_mask, eof_mask,
return tb_ring_alloc(nhi, hop, size, false, flags, e2e_tx_hop, sof_mask, eof_mask,
start_poll, poll_data);
}
EXPORT_SYMBOL_GPL(tb_ring_alloc_rx);
......@@ -629,6 +632,31 @@ void tb_ring_start(struct tb_ring *ring)
ring_iowrite32options(ring, sof_eof_mask, 4);
ring_iowrite32options(ring, flags, 0);
}
/*
* Now that the ring valid bit is set we can configure E2E if
* enabled for the ring.
*/
if (ring->flags & RING_FLAG_E2E) {
if (!ring->is_tx) {
u32 hop;
hop = ring->e2e_tx_hop << REG_RX_OPTIONS_E2E_HOP_SHIFT;
hop &= REG_RX_OPTIONS_E2E_HOP_MASK;
flags |= hop;
dev_dbg(&ring->nhi->pdev->dev,
"enabling E2E for %s %d with TX HopID %d\n",
RING_TYPE(ring), ring->hop, ring->e2e_tx_hop);
} else {
dev_dbg(&ring->nhi->pdev->dev, "enabling E2E for %s %d\n",
RING_TYPE(ring), ring->hop);
}
flags |= RING_FLAG_E2E_FLOW_CONTROL;
ring_iowrite32options(ring, flags, 0);
}
ring_interrupt_active(ring, true);
ring->running = true;
err:
......
......@@ -55,6 +55,7 @@ extern const struct tb_nhi_ops icl_nhi_ops;
* need for the PCI quirk anymore as we will use ICM also on Apple
* hardware.
*/
#define PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_4C_NHI 0x1137
#define PCI_DEVICE_ID_INTEL_WIN_RIDGE_2C_NHI 0x157d
#define PCI_DEVICE_ID_INTEL_WIN_RIDGE_2C_BRIDGE 0x157e
#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_NHI 0x15bf
......
......@@ -406,10 +406,17 @@ static int __tb_path_deactivate_hop(struct tb_port *port, int hop_index,
if (!hop.pending) {
if (clear_fc) {
/* Clear flow control */
hop.ingress_fc = 0;
/*
* Clear flow control. Protocol adapters
* IFC and ISE bits are vendor defined
* in the USB4 spec so we clear them
* only for pre-USB4 adapters.
*/
if (!tb_switch_is_usb4(port->sw)) {
hop.ingress_fc = 0;
hop.ingress_shared_buffer = 0;
}
hop.egress_fc = 0;
hop.ingress_shared_buffer = 0;
hop.egress_shared_buffer = 0;
return tb_port_write(port, &hop, TB_CFG_HOPS,
......@@ -447,7 +454,7 @@ void tb_path_deactivate(struct tb_path *path)
return;
}
tb_dbg(path->tb,
"deactivating %s path from %llx:%x to %llx:%x\n",
"deactivating %s path from %llx:%u to %llx:%u\n",
path->name, tb_route(path->hops[0].in_port->sw),
path->hops[0].in_port->port,
tb_route(path->hops[path->path_length - 1].out_port->sw),
......@@ -475,7 +482,7 @@ int tb_path_activate(struct tb_path *path)
}
tb_dbg(path->tb,
"activating %s path from %llx:%x to %llx:%x\n",
"activating %s path from %llx:%u to %llx:%u\n",
path->name, tb_route(path->hops[0].in_port->sw),
path->hops[0].in_port->port,
tb_route(path->hops[path->path_length - 1].out_port->sw),
......
......@@ -503,12 +503,13 @@ static void tb_dump_port(struct tb *tb, struct tb_regs_port_header *port)
/**
* tb_port_state() - get connectedness state of a port
* @port: the port to check
*
* The port must have a TB_CAP_PHY (i.e. it should be a real port).
*
* Return: Returns an enum tb_port_state on success or an error code on failure.
*/
static int tb_port_state(struct tb_port *port)
int tb_port_state(struct tb_port *port)
{
struct tb_cap_phy phy;
int res;
......@@ -932,7 +933,14 @@ int tb_port_get_link_speed(struct tb_port *port)
return speed == LANE_ADP_CS_1_CURRENT_SPEED_GEN3 ? 20 : 10;
}
static int tb_port_get_link_width(struct tb_port *port)
/**
* tb_port_get_link_width() - Get current link width
* @port: Port to check (USB4 or CIO)
*
* Returns link width. Return values can be 1 (Single-Lane), 2 (Dual-Lane)
* or negative errno in case of failure.
*/
int tb_port_get_link_width(struct tb_port *port)
{
u32 val;
int ret;
......@@ -1001,7 +1009,16 @@ static int tb_port_set_link_width(struct tb_port *port, unsigned int width)
port->cap_phy + LANE_ADP_CS_1, 1);
}
static int tb_port_lane_bonding_enable(struct tb_port *port)
/**
* tb_port_lane_bonding_enable() - Enable bonding on port
* @port: port to enable
*
* Enable bonding by setting the link width of the port and the
* other port in case of dual link port.
*
* Return: %0 in case of success and negative errno in case of error
*/
int tb_port_lane_bonding_enable(struct tb_port *port)
{
int ret;
......@@ -1031,7 +1048,15 @@ static int tb_port_lane_bonding_enable(struct tb_port *port)
return 0;
}
static void tb_port_lane_bonding_disable(struct tb_port *port)
/**
* tb_port_lane_bonding_disable() - Disable bonding on port
* @port: port to disable
*
* Disable bonding by setting the link width of the port and the
* other port in case of dual link port.
*
*/
void tb_port_lane_bonding_disable(struct tb_port *port)
{
port->dual_link_port->bonded = false;
port->bonded = false;
......@@ -2135,6 +2160,7 @@ static int tb_switch_add_dma_port(struct tb_switch *sw)
fallthrough;
case 3:
case 4:
ret = tb_switch_set_uuid(sw);
if (ret)
return ret;
......@@ -2150,6 +2176,22 @@ static int tb_switch_add_dma_port(struct tb_switch *sw)
break;
}
if (sw->no_nvm_upgrade)
return 0;
if (tb_switch_is_usb4(sw)) {
ret = usb4_switch_nvm_authenticate_status(sw, &status);
if (ret)
return ret;
if (status) {
tb_sw_info(sw, "switch flash authentication failed\n");
nvm_set_auth_status(sw, status);
}
return 0;
}
/* Root switch DMA port requires running firmware */
if (!tb_route(sw) && !tb_switch_is_icm(sw))
return 0;
......@@ -2158,9 +2200,6 @@ static int tb_switch_add_dma_port(struct tb_switch *sw)
if (!sw->dma_port)
return 0;
if (sw->no_nvm_upgrade)
return 0;
/*
* If there is status already set then authentication failed
* when the dma_port_flash_update_auth() returned. Power cycling
......
......@@ -1534,5 +1534,7 @@ struct tb *tb_probe(struct tb_nhi *nhi)
INIT_LIST_HEAD(&tcm->dp_resources);
INIT_DELAYED_WORK(&tcm->remove_work, tb_remove_work);
tb_dbg(tb, "using software connection manager\n");
return tb;
}
......@@ -367,6 +367,14 @@ struct tb_path {
* @disconnect_pcie_paths: Disconnects PCIe paths before NVM update
* @approve_xdomain_paths: Approve (establish) XDomain DMA paths
* @disconnect_xdomain_paths: Disconnect XDomain DMA paths
* @usb4_switch_op: Optional proxy for USB4 router operations. If set
* this will be called whenever USB4 router operation is
* performed. If this returns %-EOPNOTSUPP then the
* native USB4 router operation is called.
* @usb4_switch_nvm_authenticate_status: Optional callback that the CM
* implementation can be used to
* return status of USB4 NVM_AUTH
* router operation.
*/
struct tb_cm_ops {
int (*driver_ready)(struct tb *tb);
......@@ -393,6 +401,11 @@ struct tb_cm_ops {
int (*disconnect_pcie_paths)(struct tb *tb);
int (*approve_xdomain_paths)(struct tb *tb, struct tb_xdomain *xd);
int (*disconnect_xdomain_paths)(struct tb *tb, struct tb_xdomain *xd);
int (*usb4_switch_op)(struct tb_switch *sw, u16 opcode, u32 *metadata,
u8 *status, const void *tx_data, size_t tx_data_len,
void *rx_data, size_t rx_data_len);
int (*usb4_switch_nvm_authenticate_status)(struct tb_switch *sw,
u32 *status);
};
static inline void *tb_priv(struct tb *tb)
......@@ -864,6 +877,10 @@ struct tb_port *tb_next_port_on_path(struct tb_port *start, struct tb_port *end,
(p) = tb_next_port_on_path((src), (dst), (p)))
int tb_port_get_link_speed(struct tb_port *port);
int tb_port_get_link_width(struct tb_port *port);
int tb_port_state(struct tb_port *port);
int tb_port_lane_bonding_enable(struct tb_port *port);
void tb_port_lane_bonding_disable(struct tb_port *port);
int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec);
int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap);
......@@ -970,6 +987,7 @@ int usb4_switch_nvm_read(struct tb_switch *sw, unsigned int address, void *buf,
int usb4_switch_nvm_write(struct tb_switch *sw, unsigned int address,
const void *buf, size_t size);
int usb4_switch_nvm_authenticate(struct tb_switch *sw);
int usb4_switch_nvm_authenticate_status(struct tb_switch *sw, u32 *status);
bool usb4_switch_query_dp_resource(struct tb_switch *sw, struct tb_port *in);
int usb4_switch_alloc_dp_resource(struct tb_switch *sw, struct tb_port *in);
int usb4_switch_dealloc_dp_resource(struct tb_switch *sw, struct tb_port *in);
......@@ -1025,11 +1043,15 @@ void tb_debugfs_init(void);
void tb_debugfs_exit(void);
void tb_switch_debugfs_init(struct tb_switch *sw);
void tb_switch_debugfs_remove(struct tb_switch *sw);
void tb_service_debugfs_init(struct tb_service *svc);
void tb_service_debugfs_remove(struct tb_service *svc);
#else
static inline void tb_debugfs_init(void) { }
static inline void tb_debugfs_exit(void) { }
static inline void tb_switch_debugfs_init(struct tb_switch *sw) { }
static inline void tb_switch_debugfs_remove(struct tb_switch *sw) { }
static inline void tb_service_debugfs_init(struct tb_service *svc) { }
static inline void tb_service_debugfs_remove(struct tb_service *svc) { }
#endif
#ifdef CONFIG_USB4_KUNIT_TEST
......
......@@ -106,6 +106,7 @@ enum icm_pkg_code {
ICM_APPROVE_XDOMAIN = 0x10,
ICM_DISCONNECT_XDOMAIN = 0x11,
ICM_PREBOOT_ACL = 0x18,
ICM_USB4_SWITCH_OP = 0x20,
};
enum icm_event_code {
......@@ -343,6 +344,8 @@ struct icm_tr_pkg_driver_ready_response {
#define ICM_TR_FLAGS_RTD3 BIT(6)
#define ICM_TR_INFO_SLEVEL_MASK GENMASK(2, 0)
#define ICM_TR_INFO_PROTO_VERSION_MASK GENMASK(6, 4)
#define ICM_TR_INFO_PROTO_VERSION_SHIFT 4
#define ICM_TR_INFO_BOOT_ACL_SHIFT 7
#define ICM_TR_INFO_BOOT_ACL_MASK GENMASK(12, 7)
......@@ -478,6 +481,31 @@ struct icm_icl_event_rtd3_veto {
u32 veto_reason;
};
/* USB4 ICM messages */
struct icm_usb4_switch_op {
struct icm_pkg_header hdr;
u32 route_hi;
u32 route_lo;
u32 metadata;
u16 opcode;
u16 data_len_valid;
u32 data[16];
};
#define ICM_USB4_SWITCH_DATA_LEN_MASK GENMASK(3, 0)
#define ICM_USB4_SWITCH_DATA_VALID BIT(4)
struct icm_usb4_switch_op_response {
struct icm_pkg_header hdr;
u32 route_hi;
u32 route_lo;
u32 metadata;
u16 opcode;
u16 status;
u32 data[16];
};
/* XDomain messages */
struct tb_xdomain_header {
......
......@@ -211,11 +211,25 @@ struct tb_regs_switch_header {
#define ROUTER_CS_9 0x09
#define ROUTER_CS_25 0x19
#define ROUTER_CS_26 0x1a
#define ROUTER_CS_26_OPCODE_MASK GENMASK(15, 0)
#define ROUTER_CS_26_STATUS_MASK GENMASK(29, 24)
#define ROUTER_CS_26_STATUS_SHIFT 24
#define ROUTER_CS_26_ONS BIT(30)
#define ROUTER_CS_26_OV BIT(31)
/* USB4 router operations opcodes */
enum usb4_switch_op {
USB4_SWITCH_OP_QUERY_DP_RESOURCE = 0x10,
USB4_SWITCH_OP_ALLOC_DP_RESOURCE = 0x11,
USB4_SWITCH_OP_DEALLOC_DP_RESOURCE = 0x12,
USB4_SWITCH_OP_NVM_WRITE = 0x20,
USB4_SWITCH_OP_NVM_AUTH = 0x21,
USB4_SWITCH_OP_NVM_READ = 0x22,
USB4_SWITCH_OP_NVM_SET_OFFSET = 0x23,
USB4_SWITCH_OP_DROM_READ = 0x24,
USB4_SWITCH_OP_NVM_SECTOR_SIZE = 0x25,
};
/* Router TMU configuration */
#define TMU_RTR_CS_0 0x00
#define TMU_RTR_CS_0_TD BIT(27)
......
......@@ -34,9 +34,6 @@
#define TB_DP_AUX_PATH_OUT 1
#define TB_DP_AUX_PATH_IN 2
#define TB_DMA_PATH_OUT 0
#define TB_DMA_PATH_IN 1
static const char * const tb_tunnel_names[] = { "PCI", "DP", "DMA", "USB3" };
#define __TB_TUNNEL_PRINT(level, tunnel, fmt, arg...) \
......@@ -829,10 +826,10 @@ static void tb_dma_init_path(struct tb_path *path, unsigned int isb,
* @nhi: Host controller port
* @dst: Destination null port which the other domain is connected to
* @transmit_ring: NHI ring number used to send packets towards the
* other domain
* other domain. Set to %0 if TX path is not needed.
* @transmit_path: HopID used for transmitting packets
* @receive_ring: NHI ring number used to receive packets from the
* other domain
* other domain. Set to %0 if RX path is not needed.
* @reveive_path: HopID used for receiving packets
*
* Return: Returns a tb_tunnel on success or NULL on failure.
......@@ -843,10 +840,19 @@ struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi,
int receive_path)
{
struct tb_tunnel *tunnel;
size_t npaths = 0, i = 0;
struct tb_path *path;
u32 credits;
tunnel = tb_tunnel_alloc(tb, 2, TB_TUNNEL_DMA);
if (receive_ring)
npaths++;
if (transmit_ring)
npaths++;
if (WARN_ON(!npaths))
return NULL;
tunnel = tb_tunnel_alloc(tb, npaths, TB_TUNNEL_DMA);
if (!tunnel)
return NULL;
......@@ -856,22 +862,28 @@ struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi,
credits = tb_dma_credits(nhi);
path = tb_path_alloc(tb, dst, receive_path, nhi, receive_ring, 0, "DMA RX");
if (!path) {
tb_tunnel_free(tunnel);
return NULL;
if (receive_ring) {
path = tb_path_alloc(tb, dst, receive_path, nhi, receive_ring, 0,
"DMA RX");
if (!path) {
tb_tunnel_free(tunnel);
return NULL;
}
tb_dma_init_path(path, TB_PATH_NONE, TB_PATH_SOURCE | TB_PATH_INTERNAL,
credits);
tunnel->paths[i++] = path;
}
tb_dma_init_path(path, TB_PATH_NONE, TB_PATH_SOURCE | TB_PATH_INTERNAL,
credits);
tunnel->paths[TB_DMA_PATH_IN] = path;
path = tb_path_alloc(tb, nhi, transmit_ring, dst, transmit_path, 0, "DMA TX");
if (!path) {
tb_tunnel_free(tunnel);
return NULL;
if (transmit_ring) {
path = tb_path_alloc(tb, nhi, transmit_ring, dst, transmit_path, 0,
"DMA TX");
if (!path) {
tb_tunnel_free(tunnel);
return NULL;
}
tb_dma_init_path(path, TB_PATH_SOURCE, TB_PATH_ALL, credits);
tunnel->paths[i++] = path;
}
tb_dma_init_path(path, TB_PATH_SOURCE, TB_PATH_ALL, credits);
tunnel->paths[TB_DMA_PATH_OUT] = path;
return tunnel;
}
......
This diff is collapsed.
......@@ -8,6 +8,7 @@
*/
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/kmod.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
......@@ -21,6 +22,7 @@
#define XDOMAIN_UUID_RETRIES 10
#define XDOMAIN_PROPERTIES_RETRIES 60
#define XDOMAIN_PROPERTIES_CHANGED_RETRIES 10
#define XDOMAIN_BONDING_WAIT 100 /* ms */
struct xdomain_request_work {
struct work_struct work;
......@@ -587,8 +589,6 @@ static void tb_xdp_handle_request(struct work_struct *work)
break;
case PROPERTIES_CHANGED_REQUEST: {
const struct tb_xdp_properties_changed *xchg =
(const struct tb_xdp_properties_changed *)pkg;
struct tb_xdomain *xd;
ret = tb_xdp_properties_changed_response(ctl, route, sequence);
......@@ -598,10 +598,12 @@ static void tb_xdp_handle_request(struct work_struct *work)
* the xdomain related to this connection as well in
* case there is a change in services it offers.
*/
xd = tb_xdomain_find_by_uuid_locked(tb, &xchg->src_uuid);
xd = tb_xdomain_find_by_route_locked(tb, route);
if (xd) {
queue_delayed_work(tb->wq, &xd->get_properties_work,
msecs_to_jiffies(50));
if (device_is_registered(&xd->dev)) {
queue_delayed_work(tb->wq, &xd->get_properties_work,
msecs_to_jiffies(50));
}
tb_xdomain_put(xd);
}
......@@ -777,6 +779,7 @@ static void tb_service_release(struct device *dev)
struct tb_service *svc = container_of(dev, struct tb_service, dev);
struct tb_xdomain *xd = tb_service_parent(svc);
tb_service_debugfs_remove(svc);
ida_simple_remove(&xd->service_ids, svc->id);
kfree(svc->key);
kfree(svc);
......@@ -891,6 +894,8 @@ static void enumerate_services(struct tb_xdomain *xd)
svc->dev.parent = &xd->dev;
dev_set_name(&svc->dev, "%s.%d", dev_name(&xd->dev), svc->id);
tb_service_debugfs_init(svc);
if (device_register(&svc->dev)) {
put_device(&svc->dev);
break;
......@@ -943,6 +948,43 @@ static void tb_xdomain_restore_paths(struct tb_xdomain *xd)
}
}
static inline struct tb_switch *tb_xdomain_parent(struct tb_xdomain *xd)
{
return tb_to_switch(xd->dev.parent);
}
static int tb_xdomain_update_link_attributes(struct tb_xdomain *xd)
{
bool change = false;
struct tb_port *port;
int ret;
port = tb_port_at(xd->route, tb_xdomain_parent(xd));
ret = tb_port_get_link_speed(port);
if (ret < 0)
return ret;
if (xd->link_speed != ret)
change = true;
xd->link_speed = ret;
ret = tb_port_get_link_width(port);
if (ret < 0)
return ret;
if (xd->link_width != ret)
change = true;
xd->link_width = ret;
if (change)
kobject_uevent(&xd->dev.kobj, KOBJ_CHANGE);
return 0;
}
static void tb_xdomain_get_uuid(struct work_struct *work)
{
struct tb_xdomain *xd = container_of(work, typeof(*xd),
......@@ -962,10 +1004,8 @@ static void tb_xdomain_get_uuid(struct work_struct *work)
return;
}
if (uuid_equal(&uuid, xd->local_uuid)) {
if (uuid_equal(&uuid, xd->local_uuid))
dev_dbg(&xd->dev, "intra-domain loop detected\n");
return;
}
/*
* If the UUID is different, there is another domain connected
......@@ -1056,6 +1096,8 @@ static void tb_xdomain_get_properties(struct work_struct *work)
xd->properties = dir;
xd->property_block_gen = gen;
tb_xdomain_update_link_attributes(xd);
tb_xdomain_restore_paths(xd);
mutex_unlock(&xd->lock);
......@@ -1162,9 +1204,35 @@ static ssize_t unique_id_show(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_RO(unique_id);
static ssize_t speed_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev);
return sprintf(buf, "%u.0 Gb/s\n", xd->link_speed);
}
static DEVICE_ATTR(rx_speed, 0444, speed_show, NULL);
static DEVICE_ATTR(tx_speed, 0444, speed_show, NULL);
static ssize_t lanes_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev);
return sprintf(buf, "%u\n", xd->link_width);
}
static DEVICE_ATTR(rx_lanes, 0444, lanes_show, NULL);
static DEVICE_ATTR(tx_lanes, 0444, lanes_show, NULL);
static struct attribute *xdomain_attrs[] = {
&dev_attr_device.attr,
&dev_attr_device_name.attr,
&dev_attr_rx_lanes.attr,
&dev_attr_rx_speed.attr,
&dev_attr_tx_lanes.attr,
&dev_attr_tx_speed.attr,
&dev_attr_unique_id.attr,
&dev_attr_vendor.attr,
&dev_attr_vendor_name.attr,
......@@ -1381,6 +1449,70 @@ void tb_xdomain_remove(struct tb_xdomain *xd)
device_unregister(&xd->dev);
}
/**
* tb_xdomain_lane_bonding_enable() - Enable lane bonding on XDomain
* @xd: XDomain connection
*
* Lane bonding is disabled by default for XDomains. This function tries
* to enable bonding by first enabling the port and waiting for the CL0
* state.
*
* Return: %0 in case of success and negative errno in case of error.
*/
int tb_xdomain_lane_bonding_enable(struct tb_xdomain *xd)
{
struct tb_port *port;
int ret;
port = tb_port_at(xd->route, tb_xdomain_parent(xd));
if (!port->dual_link_port)
return -ENODEV;
ret = tb_port_enable(port->dual_link_port);
if (ret)
return ret;
ret = tb_wait_for_port(port->dual_link_port, true);
if (ret < 0)
return ret;
if (!ret)
return -ENOTCONN;
ret = tb_port_lane_bonding_enable(port);
if (ret) {
tb_port_warn(port, "failed to enable lane bonding\n");
return ret;
}
tb_xdomain_update_link_attributes(xd);
dev_dbg(&xd->dev, "lane bonding enabled\n");
return 0;
}
EXPORT_SYMBOL_GPL(tb_xdomain_lane_bonding_enable);
/**
* tb_xdomain_lane_bonding_disable() - Disable lane bonding
* @xd: XDomain connection
*
* Lane bonding is disabled by default for XDomains. If bonding has been
* enabled, this function can be used to disable it.
*/
void tb_xdomain_lane_bonding_disable(struct tb_xdomain *xd)
{
struct tb_port *port;
port = tb_port_at(xd->route, tb_xdomain_parent(xd));
if (port->dual_link_port) {
tb_port_lane_bonding_disable(port);
tb_port_disable(port->dual_link_port);
tb_xdomain_update_link_attributes(xd);
dev_dbg(&xd->dev, "lane bonding disabled\n");
}
}
EXPORT_SYMBOL_GPL(tb_xdomain_lane_bonding_disable);
/**
* tb_xdomain_enable_paths() - Enable DMA paths for XDomain connection
* @xd: XDomain connection
......
......@@ -179,6 +179,8 @@ void tb_unregister_property_dir(const char *key, struct tb_property_dir *dir);
* @lock: Lock to serialize access to the following fields of this structure
* @vendor_name: Name of the vendor (or %NULL if not known)
* @device_name: Name of the device (or %NULL if not known)
* @link_speed: Speed of the link in Gb/s
* @link_width: Width of the link (1 or 2)
* @is_unplugged: The XDomain is unplugged
* @resume: The XDomain is being resumed
* @needs_uuid: If the XDomain does not have @remote_uuid it will be
......@@ -223,6 +225,8 @@ struct tb_xdomain {
struct mutex lock;
const char *vendor_name;
const char *device_name;
unsigned int link_speed;
unsigned int link_width;
bool is_unplugged;
bool resume;
bool needs_uuid;
......@@ -243,6 +247,8 @@ struct tb_xdomain {
u8 depth;
};
int tb_xdomain_lane_bonding_enable(struct tb_xdomain *xd);
void tb_xdomain_lane_bonding_disable(struct tb_xdomain *xd);
int tb_xdomain_enable_paths(struct tb_xdomain *xd, u16 transmit_path,
u16 transmit_ring, u16 receive_path,
u16 receive_ring);
......@@ -344,6 +350,9 @@ void tb_unregister_protocol_handler(struct tb_protocol_handler *handler);
* @prtcvers: Protocol version from the properties directory
* @prtcrevs: Protocol software revision from the properties directory
* @prtcstns: Protocol settings mask from the properties directory
* @debugfs_dir: Pointer to the service debugfs directory. Always created
* when debugfs is enabled. Can be used by service drivers to
* add their own entries under the service.
*
* Each domain exposes set of services it supports as collection of
* properties. For each service there will be one corresponding
......@@ -357,6 +366,7 @@ struct tb_service {
u32 prtcvers;
u32 prtcrevs;
u32 prtcstns;
struct dentry *debugfs_dir;
};
static inline struct tb_service *tb_service_get(struct tb_service *svc)
......@@ -471,6 +481,8 @@ struct tb_nhi {
* @irq: MSI-X irq number if the ring uses MSI-X. %0 otherwise.
* @vector: MSI-X vector number the ring uses (only set if @irq is > 0)
* @flags: Ring specific flags
* @e2e_tx_hop: Transmit HopID when E2E is enabled. Only applicable to
* RX ring. For TX ring this should be set to %0.
* @sof_mask: Bit mask used to detect start of frame PDF
* @eof_mask: Bit mask used to detect end of frame PDF
* @start_poll: Called when ring interrupt is triggered to start
......@@ -494,6 +506,7 @@ struct tb_ring {
int irq;
u8 vector;
unsigned int flags;
int e2e_tx_hop;
u16 sof_mask;
u16 eof_mask;
void (*start_poll)(void *data);
......@@ -504,6 +517,8 @@ struct tb_ring {
#define RING_FLAG_NO_SUSPEND BIT(0)
/* Configure the ring to be in frame mode */
#define RING_FLAG_FRAME BIT(1)
/* Enable end-to-end flow control */
#define RING_FLAG_E2E BIT(2)
struct ring_frame;
typedef void (*ring_cb)(struct tb_ring *, struct ring_frame *, bool canceled);
......@@ -552,7 +567,8 @@ struct ring_frame {
struct tb_ring *tb_ring_alloc_tx(struct tb_nhi *nhi, int hop, int size,
unsigned int flags);
struct tb_ring *tb_ring_alloc_rx(struct tb_nhi *nhi, int hop, int size,
unsigned int flags, u16 sof_mask, u16 eof_mask,
unsigned int flags, int e2e_tx_hop,
u16 sof_mask, u16 eof_mask,
void (*start_poll)(void *), void *poll_data);
void tb_ring_start(struct tb_ring *ring);
void tb_ring_stop(struct tb_ring *ring);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment