summaryrefslogtreecommitdiff
path: root/drivers/thunderbolt/icm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/thunderbolt/icm.c')
-rw-r--r--drivers/thunderbolt/icm.c33
1 files changed, 32 insertions, 1 deletions
diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c
index 0ffa4ec249ac..8ee340290219 100644
--- a/drivers/thunderbolt/icm.c
+++ b/drivers/thunderbolt/icm.c
@@ -54,6 +54,7 @@
* where ICM needs to be started manually
* @vnd_cap: Vendor defined capability where PCIe2CIO mailbox resides
* (only set when @upstream_port is not %NULL)
+ * @safe_mode: ICM is in safe mode
* @is_supported: Checks if we can support ICM on this controller
* @get_mode: Read and return the ICM firmware mode (optional)
* @get_route: Find a route string for given switch
@@ -65,6 +66,7 @@ struct icm {
struct delayed_work rescan_work;
struct pci_dev *upstream_port;
int vnd_cap;
+ bool safe_mode;
bool (*is_supported)(struct tb *tb);
int (*get_mode)(struct tb *tb);
int (*get_route)(struct tb *tb, u8 link, u8 depth, u64 *route);
@@ -852,6 +854,10 @@ static int icm_firmware_init(struct tb *tb)
ret = icm->get_mode(tb);
switch (ret) {
+ case NHI_FW_SAFE_MODE:
+ icm->safe_mode = true;
+ break;
+
case NHI_FW_CM_MODE:
/* Ask ICM to accept all Thunderbolt devices */
nhi_mailbox_cmd(nhi, NHI_MAILBOX_ALLOW_ALL_DEVS, 0);
@@ -879,12 +885,20 @@ static int icm_firmware_init(struct tb *tb)
static int icm_driver_ready(struct tb *tb)
{
+ struct icm *icm = tb_priv(tb);
int ret;
ret = icm_firmware_init(tb);
if (ret)
return ret;
+ if (icm->safe_mode) {
+ tb_info(tb, "Thunderbolt host controller is in safe mode.\n");
+ tb_info(tb, "You need to update NVM firmware of the controller before it can be used.\n");
+ tb_info(tb, "For latest updates check https://thunderbolttechnology.net/updates.\n");
+ return 0;
+ }
+
return __icm_driver_ready(tb, &tb->security_level);
}
@@ -975,12 +989,23 @@ static void icm_complete(struct tb *tb)
static int icm_start(struct tb *tb)
{
+ struct icm *icm = tb_priv(tb);
int ret;
- tb->root_switch = tb_switch_alloc(tb, &tb->dev, 0);
+ if (icm->safe_mode)
+ tb->root_switch = tb_switch_alloc_safe_mode(tb, &tb->dev, 0);
+ else
+ tb->root_switch = tb_switch_alloc(tb, &tb->dev, 0);
if (!tb->root_switch)
return -ENODEV;
+ /*
+ * NVM upgrade has not been tested on Apple systems and they
+ * don't provide images publicly either. To be on the safe side
+ * prevent root switch NVM upgrade on Macs for now.
+ */
+ tb->root_switch->no_nvm_upgrade = is_apple();
+
ret = tb_switch_add(tb->root_switch);
if (ret)
tb_switch_put(tb->root_switch);
@@ -998,6 +1023,11 @@ static void icm_stop(struct tb *tb)
nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DRV_UNLOADS, 0);
}
+static int icm_disconnect_pcie_paths(struct tb *tb)
+{
+ return nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DISCONNECT_PCIE_PATHS, 0);
+}
+
/* Falcon Ridge and Alpine Ridge */
static const struct tb_cm_ops icm_fr_ops = {
.driver_ready = icm_driver_ready,
@@ -1009,6 +1039,7 @@ static const struct tb_cm_ops icm_fr_ops = {
.approve_switch = icm_fr_approve_switch,
.add_switch_key = icm_fr_add_switch_key,
.challenge_switch_key = icm_fr_challenge_switch_key,
+ .disconnect_pcie_paths = icm_disconnect_pcie_paths,
};
struct tb *icm_probe(struct tb_nhi *nhi)