return (u64)route_hi << 32 | route_lo;
 }
 
+static inline u64 get_parent_route(u64 route)
+{
+       int depth = tb_route_length(route);
+       return depth ? route & ~(0xffULL << (depth - 1) * TB_ROUTE_SHIFT) : 0;
+}
+
 static bool icm_match(const struct tb_cfg_request *req,
                      const struct ctl_pkg *pkg)
 {
        }
 }
 
+static int
+icm_tr_driver_ready(struct tb *tb, enum tb_security_level *security_level,
+                   size_t *nboot_acl)
+{
+       struct icm_tr_pkg_driver_ready_response reply;
+       struct icm_pkg_driver_ready request = {
+               .hdr.code = ICM_DRIVER_READY,
+       };
+       int ret;
+
+       memset(&reply, 0, sizeof(reply));
+       ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+                         1, 20000);
+       if (ret)
+               return ret;
+
+       if (security_level)
+               *security_level = reply.info & ICM_TR_INFO_SLEVEL_MASK;
+       if (nboot_acl)
+               *nboot_acl = (reply.info & ICM_TR_INFO_BOOT_ACL_MASK) >>
+                               ICM_TR_INFO_BOOT_ACL_SHIFT;
+       return 0;
+}
+
+static int icm_tr_approve_switch(struct tb *tb, struct tb_switch *sw)
+{
+       struct icm_tr_pkg_approve_device request;
+       struct icm_tr_pkg_approve_device reply;
+       int ret;
+
+       memset(&request, 0, sizeof(request));
+       memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid));
+       request.hdr.code = ICM_APPROVE_DEVICE;
+       request.route_lo = sw->config.route_lo;
+       request.route_hi = sw->config.route_hi;
+       request.connection_id = sw->connection_id;
+
+       memset(&reply, 0, sizeof(reply));
+       ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+                         1, ICM_APPROVE_TIMEOUT);
+       if (ret)
+               return ret;
+
+       if (reply.hdr.flags & ICM_FLAGS_ERROR) {
+               tb_warn(tb, "PCIe tunnel creation failed\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int icm_tr_add_switch_key(struct tb *tb, struct tb_switch *sw)
+{
+       struct icm_tr_pkg_add_device_key_response reply;
+       struct icm_tr_pkg_add_device_key request;
+       int ret;
+
+       memset(&request, 0, sizeof(request));
+       memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid));
+       request.hdr.code = ICM_ADD_DEVICE_KEY;
+       request.route_lo = sw->config.route_lo;
+       request.route_hi = sw->config.route_hi;
+       request.connection_id = sw->connection_id;
+       memcpy(request.key, sw->key, TB_SWITCH_KEY_SIZE);
+
+       memset(&reply, 0, sizeof(reply));
+       ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+                         1, ICM_TIMEOUT);
+       if (ret)
+               return ret;
+
+       if (reply.hdr.flags & ICM_FLAGS_ERROR) {
+               tb_warn(tb, "Adding key to switch failed\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int icm_tr_challenge_switch_key(struct tb *tb, struct tb_switch *sw,
+                                      const u8 *challenge, u8 *response)
+{
+       struct icm_tr_pkg_challenge_device_response reply;
+       struct icm_tr_pkg_challenge_device request;
+       int ret;
+
+       memset(&request, 0, sizeof(request));
+       memcpy(&request.ep_uuid, sw->uuid, sizeof(request.ep_uuid));
+       request.hdr.code = ICM_CHALLENGE_DEVICE;
+       request.route_lo = sw->config.route_lo;
+       request.route_hi = sw->config.route_hi;
+       request.connection_id = sw->connection_id;
+       memcpy(request.challenge, challenge, TB_SWITCH_KEY_SIZE);
+
+       memset(&reply, 0, sizeof(reply));
+       ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+                         1, ICM_TIMEOUT);
+       if (ret)
+               return ret;
+
+       if (reply.hdr.flags & ICM_FLAGS_ERROR)
+               return -EKEYREJECTED;
+       if (reply.hdr.flags & ICM_FLAGS_NO_KEY)
+               return -ENOKEY;
+
+       memcpy(response, reply.response, TB_SWITCH_KEY_SIZE);
+
+       return 0;
+}
+
+static int icm_tr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd)
+{
+       struct icm_tr_pkg_approve_xdomain_response reply;
+       struct icm_tr_pkg_approve_xdomain request;
+       int ret;
+
+       memset(&request, 0, sizeof(request));
+       request.hdr.code = ICM_APPROVE_XDOMAIN;
+       request.route_hi = upper_32_bits(xd->route);
+       request.route_lo = lower_32_bits(xd->route);
+       request.transmit_path = xd->transmit_path;
+       request.transmit_ring = xd->transmit_ring;
+       request.receive_path = xd->receive_path;
+       request.receive_ring = xd->receive_ring;
+       memcpy(&request.remote_uuid, xd->remote_uuid, sizeof(*xd->remote_uuid));
+
+       memset(&reply, 0, sizeof(reply));
+       ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+                         1, ICM_TIMEOUT);
+       if (ret)
+               return ret;
+
+       if (reply.hdr.flags & ICM_FLAGS_ERROR)
+               return -EIO;
+
+       return 0;
+}
+
+static int icm_tr_xdomain_tear_down(struct tb *tb, struct tb_xdomain *xd,
+                                   int stage)
+{
+       struct icm_tr_pkg_disconnect_xdomain_response reply;
+       struct icm_tr_pkg_disconnect_xdomain request;
+       int ret;
+
+       memset(&request, 0, sizeof(request));
+       request.hdr.code = ICM_DISCONNECT_XDOMAIN;
+       request.stage = stage;
+       request.route_hi = upper_32_bits(xd->route);
+       request.route_lo = lower_32_bits(xd->route);
+       memcpy(&request.remote_uuid, xd->remote_uuid, sizeof(*xd->remote_uuid));
+
+       memset(&reply, 0, sizeof(reply));
+       ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+                         1, ICM_TIMEOUT);
+       if (ret)
+               return ret;
+
+       if (reply.hdr.flags & ICM_FLAGS_ERROR)
+               return -EIO;
+
+       return 0;
+}
+
+static int icm_tr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd)
+{
+       int ret;
+
+       ret = icm_tr_xdomain_tear_down(tb, xd, 1);
+       if (ret)
+               return ret;
+
+       usleep_range(10, 50);
+       return icm_tr_xdomain_tear_down(tb, xd, 2);
+}
+
+static void
+icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
+{
+       const struct icm_tr_event_device_connected *pkg =
+               (const struct icm_tr_event_device_connected *)hdr;
+       enum tb_security_level security_level;
+       struct tb_switch *sw, *parent_sw;
+       struct tb_xdomain *xd;
+       bool authorized, boot;
+       u64 route;
+
+       /*
+        * Currently we don't use the QoS information coming with the
+        * device connected message so simply just ignore that extra
+        * packet for now.
+        */
+       if (pkg->hdr.packet_id)
+               return;
+
+       /*
+        * After NVM upgrade adding root switch device fails because we
+        * initiated reset. During that time ICM might still send device
+        * connected message which we ignore here.
+        */
+       if (!tb->root_switch)
+               return;
+
+       route = get_route(pkg->route_hi, pkg->route_lo);
+       authorized = pkg->link_info & ICM_LINK_INFO_APPROVED;
+       security_level = (pkg->hdr.flags & ICM_FLAGS_SLEVEL_MASK) >>
+                        ICM_FLAGS_SLEVEL_SHIFT;
+       boot = pkg->link_info & ICM_LINK_INFO_BOOT;
+
+       if (pkg->link_info & ICM_LINK_INFO_REJECTED) {
+               tb_info(tb, "switch at %llx was rejected by ICM firmware because topology limit exceeded\n",
+                       route);
+               return;
+       }
+
+       sw = tb_switch_find_by_uuid(tb, &pkg->ep_uuid);
+       if (sw) {
+               /* Update the switch if it is still in the same place */
+               if (tb_route(sw) == route && !!sw->authorized == authorized) {
+                       parent_sw = tb_to_switch(sw->dev.parent);
+                       update_switch(parent_sw, sw, route, pkg->connection_id,
+                                     0, 0, 0, boot);
+                       tb_switch_put(sw);
+                       return;
+               }
+
+               remove_switch(sw);
+               tb_switch_put(sw);
+       }
+
+       /* Another switch with the same address */
+       sw = tb_switch_find_by_route(tb, route);
+       if (sw) {
+               remove_switch(sw);
+               tb_switch_put(sw);
+       }
+
+       /* XDomain connection with the same address */
+       xd = tb_xdomain_find_by_route(tb, route);
+       if (xd) {
+               remove_xdomain(xd);
+               tb_xdomain_put(xd);
+       }
+
+       parent_sw = tb_switch_find_by_route(tb, get_parent_route(route));
+       if (!parent_sw) {
+               tb_err(tb, "failed to find parent switch for %llx\n", route);
+               return;
+       }
+
+       add_switch(parent_sw, route, &pkg->ep_uuid, pkg->connection_id,
+                  0, 0, 0, security_level, authorized, boot);
+
+       tb_switch_put(parent_sw);
+}
+
+static void
+icm_tr_device_disconnected(struct tb *tb, const struct icm_pkg_header *hdr)
+{
+       const struct icm_tr_event_device_disconnected *pkg =
+               (const struct icm_tr_event_device_disconnected *)hdr;
+       struct tb_switch *sw;
+       u64 route;
+
+       route = get_route(pkg->route_hi, pkg->route_lo);
+
+       sw = tb_switch_find_by_route(tb, route);
+       if (!sw) {
+               tb_warn(tb, "no switch exists at %llx, ignoring\n", route);
+               return;
+       }
+
+       remove_switch(sw);
+       tb_switch_put(sw);
+}
+
+static void
+icm_tr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr)
+{
+       const struct icm_tr_event_xdomain_connected *pkg =
+               (const struct icm_tr_event_xdomain_connected *)hdr;
+       struct tb_xdomain *xd;
+       struct tb_switch *sw;
+       u64 route;
+
+       if (!tb->root_switch)
+               return;
+
+       route = get_route(pkg->local_route_hi, pkg->local_route_lo);
+
+       xd = tb_xdomain_find_by_uuid(tb, &pkg->remote_uuid);
+       if (xd) {
+               if (xd->route == route) {
+                       update_xdomain(xd, route, 0);
+                       tb_xdomain_put(xd);
+                       return;
+               }
+
+               remove_xdomain(xd);
+               tb_xdomain_put(xd);
+       }
+
+       /* An existing xdomain with the same address */
+       xd = tb_xdomain_find_by_route(tb, route);
+       if (xd) {
+               remove_xdomain(xd);
+               tb_xdomain_put(xd);
+       }
+
+       /*
+        * If the user disconnected a switch during suspend and
+        * connected another host to the same port, remove the switch
+        * first.
+        */
+       sw = get_switch_at_route(tb->root_switch, route);
+       if (sw)
+               remove_switch(sw);
+
+       sw = tb_switch_find_by_route(tb, get_parent_route(route));
+       if (!sw) {
+               tb_warn(tb, "no switch exists at %llx, ignoring\n", route);
+               return;
+       }
+
+       add_xdomain(sw, route, &pkg->local_uuid, &pkg->remote_uuid, 0, 0);
+       tb_switch_put(sw);
+}
+
+static void
+icm_tr_xdomain_disconnected(struct tb *tb, const struct icm_pkg_header *hdr)
+{
+       const struct icm_tr_event_xdomain_disconnected *pkg =
+               (const struct icm_tr_event_xdomain_disconnected *)hdr;
+       struct tb_xdomain *xd;
+       u64 route;
+
+       route = get_route(pkg->route_hi, pkg->route_lo);
+
+       xd = tb_xdomain_find_by_route(tb, route);
+       if (xd) {
+               remove_xdomain(xd);
+               tb_xdomain_put(xd);
+       }
+}
+
 static struct pci_dev *get_upstream_port(struct pci_dev *pdev)
 {
        struct pci_dev *parent;
        .disconnect_xdomain_paths = icm_fr_disconnect_xdomain_paths,
 };
 
+/* Titan Ridge */
+static const struct tb_cm_ops icm_tr_ops = {
+       .driver_ready = icm_driver_ready,
+       .start = icm_start,
+       .stop = icm_stop,
+       .suspend = icm_suspend,
+       .complete = icm_complete,
+       .handle_event = icm_handle_event,
+       .get_boot_acl = icm_ar_get_boot_acl,
+       .set_boot_acl = icm_ar_set_boot_acl,
+       .approve_switch = icm_tr_approve_switch,
+       .add_switch_key = icm_tr_add_switch_key,
+       .challenge_switch_key = icm_tr_challenge_switch_key,
+       .disconnect_pcie_paths = icm_disconnect_pcie_paths,
+       .approve_xdomain_paths = icm_tr_approve_xdomain_paths,
+       .disconnect_xdomain_paths = icm_tr_disconnect_xdomain_paths,
+};
+
 struct tb *icm_probe(struct tb_nhi *nhi)
 {
        struct icm *icm;
                icm->xdomain_disconnected = icm_fr_xdomain_disconnected;
                tb->cm_ops = &icm_ar_ops;
                break;
+
+       case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_NHI:
+       case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI:
+               icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES;
+               icm->is_supported = icm_ar_is_supported;
+               icm->get_mode = icm_ar_get_mode;
+               icm->driver_ready = icm_tr_driver_ready;
+               icm->device_connected = icm_tr_device_connected;
+               icm->device_disconnected = icm_tr_device_disconnected;
+               icm->xdomain_connected = icm_tr_xdomain_connected;
+               icm->xdomain_disconnected = icm_tr_xdomain_disconnected;
+               tb->cm_ops = &icm_tr_ops;
+               break;
        }
 
        if (!icm->is_supported || !icm->is_supported(tb)) {