diff options
Diffstat (limited to 'drivers/thunderbolt/switch.c')
| -rw-r--r-- | drivers/thunderbolt/switch.c | 93 | 
1 files changed, 59 insertions, 34 deletions
| diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index b63fecca6c2a..e73cd296db7e 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -627,28 +627,6 @@ int tb_port_add_nfc_credits(struct tb_port *port, int credits)  }  /** - * tb_port_set_initial_credits() - Set initial port link credits allocated - * @port: Port to set the initial credits - * @credits: Number of credits to to allocate - * - * Set initial credits value to be used for ingress shared buffering. - */ -int tb_port_set_initial_credits(struct tb_port *port, u32 credits) -{ -	u32 data; -	int ret; - -	ret = tb_port_read(port, &data, TB_CFG_PORT, ADP_CS_5, 1); -	if (ret) -		return ret; - -	data &= ~ADP_CS_5_LCA_MASK; -	data |= (credits << ADP_CS_5_LCA_SHIFT) & ADP_CS_5_LCA_MASK; - -	return tb_port_write(port, &data, TB_CFG_PORT, ADP_CS_5, 1); -} - -/**   * tb_port_clear_counter() - clear a counter in TB_CFG_COUNTER   * @port: Port whose counters to clear   * @counter: Counter index to clear @@ -768,12 +746,6 @@ static int tb_init_port(struct tb_port *port)  	tb_dump_port(port->sw->tb, &port->config); -	/* Control port does not need HopID allocation */ -	if (port->port) { -		ida_init(&port->in_hopids); -		ida_init(&port->out_hopids); -	} -  	INIT_LIST_HEAD(&port->list);  	return 0; @@ -1337,7 +1309,7 @@ int tb_switch_reset(struct tb_switch *sw)  			      TB_CFG_SWITCH, 2, 2);  	if (res.err)  		return res.err; -	res = tb_cfg_reset(sw->tb->ctl, tb_route(sw), TB_CFG_DEFAULT_TIMEOUT); +	res = tb_cfg_reset(sw->tb->ctl, tb_route(sw));  	if (res.err > 0)  		return -EIO;  	return res.err; @@ -1768,6 +1740,18 @@ static struct attribute *switch_attrs[] = {  	NULL,  }; +static bool has_port(const struct tb_switch *sw, enum tb_port_type type) +{ +	const struct tb_port *port; + +	tb_switch_for_each_port(sw, port) { +		if (!port->disabled && port->config.type == type) +			return true; +	} + +	return false; +} +  static umode_t switch_attr_is_visible(struct kobject *kobj,  				      struct attribute *attr, int n)  { @@ -1776,7 +1760,8 @@ static umode_t switch_attr_is_visible(struct kobject *kobj,  	if (attr == &dev_attr_authorized.attr) {  		if (sw->tb->security_level == TB_SECURITY_NOPCIE || -		    sw->tb->security_level == TB_SECURITY_DPONLY) +		    sw->tb->security_level == TB_SECURITY_DPONLY || +		    !has_port(sw, TB_TYPE_PCIE_UP))  			return 0;  	} else if (attr == &dev_attr_device.attr) {  		if (!sw->device) @@ -1842,10 +1827,8 @@ static void tb_switch_release(struct device *dev)  	dma_port_free(sw->dma_port);  	tb_switch_for_each_port(sw, port) { -		if (!port->disabled) { -			ida_destroy(&port->in_hopids); -			ida_destroy(&port->out_hopids); -		} +		ida_destroy(&port->in_hopids); +		ida_destroy(&port->out_hopids);  	}  	kfree(sw->uuid); @@ -1857,6 +1840,39 @@ static void tb_switch_release(struct device *dev)  	kfree(sw);  } +static int tb_switch_uevent(struct device *dev, struct kobj_uevent_env *env) +{ +	struct tb_switch *sw = tb_to_switch(dev); +	const char *type; + +	if (sw->config.thunderbolt_version == USB4_VERSION_1_0) { +		if (add_uevent_var(env, "USB4_VERSION=1.0")) +			return -ENOMEM; +	} + +	if (!tb_route(sw)) { +		type = "host"; +	} else { +		const struct tb_port *port; +		bool hub = false; + +		/* Device is hub if it has any downstream ports */ +		tb_switch_for_each_port(sw, port) { +			if (!port->disabled && !tb_is_upstream_port(port) && +			     tb_port_is_null(port)) { +				hub = true; +				break; +			} +		} + +		type = hub ? "hub" : "device"; +	} + +	if (add_uevent_var(env, "USB4_TYPE=%s", type)) +		return -ENOMEM; +	return 0; +} +  /*   * Currently only need to provide the callbacks. Everything else is handled   * in the connection manager. @@ -1890,6 +1906,7 @@ static const struct dev_pm_ops tb_switch_pm_ops = {  struct device_type tb_switch_type = {  	.name = "thunderbolt_device",  	.release = tb_switch_release, +	.uevent = tb_switch_uevent,  	.pm = &tb_switch_pm_ops,  }; @@ -2025,6 +2042,12 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, struct device *parent,  		/* minimum setup for tb_find_cap and tb_drom_read to work */  		sw->ports[i].sw = sw;  		sw->ports[i].port = i; + +		/* Control port does not need HopID allocation */ +		if (i) { +			ida_init(&sw->ports[i].in_hopids); +			ida_init(&sw->ports[i].out_hopids); +		}  	}  	ret = tb_switch_find_vse_cap(sw, TB_VSE_CAP_PLUG_EVENTS); @@ -2544,6 +2567,8 @@ int tb_switch_add(struct tb_switch *sw)  		}  		tb_sw_dbg(sw, "uid: %#llx\n", sw->uid); +		tb_check_quirks(sw); +  		ret = tb_switch_set_uuid(sw);  		if (ret) {  			dev_err(&sw->dev, "failed to set UUID\n"); | 
