mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge branch 'next' of https://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt.git
# Conflicts: # drivers/thunderbolt/nhi.c
This commit is contained in:
@@ -1340,7 +1340,7 @@ static void tbnet_generate_mac(struct net_device *dev)
|
||||
dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
|
||||
}
|
||||
|
||||
static int tbnet_probe(struct tb_service *svc, const struct tb_service_id *id)
|
||||
static int tbnet_probe(struct tb_service *svc)
|
||||
{
|
||||
struct tb_xdomain *xd = tb_service_parent(svc);
|
||||
struct net_device *dev;
|
||||
@@ -1460,7 +1460,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(tbnet_pm_ops, tbnet_suspend, tbnet_resume);
|
||||
|
||||
static const struct tb_service_id tbnet_ids[] = {
|
||||
{ TB_SERVICE("network", 1) },
|
||||
{ },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(tbsvc, tbnet_ids);
|
||||
|
||||
|
||||
@@ -636,7 +636,7 @@ static void dma_test_debugfs_init(struct tb_service *svc)
|
||||
debugfs_create_file("test", 0200, debugfs_dir, svc, &test_fops);
|
||||
}
|
||||
|
||||
static int dma_test_probe(struct tb_service *svc, const struct tb_service_id *id)
|
||||
static int dma_test_probe(struct tb_service *svc)
|
||||
{
|
||||
struct tb_xdomain *xd = tb_service_parent(svc);
|
||||
struct dma_test *dt;
|
||||
@@ -689,7 +689,7 @@ static const struct dev_pm_ops dma_test_pm_ops = {
|
||||
|
||||
static const struct tb_service_id dma_test_ids[] = {
|
||||
{ TB_SERVICE("dma_test", 1) },
|
||||
{ },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(tbsvc, dma_test_ids);
|
||||
|
||||
|
||||
@@ -77,12 +77,10 @@ static int tb_service_probe(struct device *dev)
|
||||
{
|
||||
struct tb_service *svc = tb_to_service(dev);
|
||||
struct tb_service_driver *driver;
|
||||
const struct tb_service_id *id;
|
||||
|
||||
driver = container_of(dev->driver, struct tb_service_driver, driver);
|
||||
id = __tb_service_match(dev, &driver->driver);
|
||||
|
||||
return driver->probe(svc, id);
|
||||
return driver->probe(svc);
|
||||
}
|
||||
|
||||
static void tb_service_remove(struct device *dev)
|
||||
|
||||
@@ -1235,6 +1235,8 @@ int nhi_probe(struct tb_nhi *nhi)
|
||||
|
||||
dev_dbg(dev, "NHI initialized, starting thunderbolt\n");
|
||||
|
||||
nhi->host_reset = host_reset;
|
||||
|
||||
res = tb_domain_add(tb, host_reset);
|
||||
if (res) {
|
||||
/*
|
||||
|
||||
@@ -112,7 +112,6 @@ static int nhi_pci_init_msi(struct tb_nhi *nhi)
|
||||
{
|
||||
struct tb_nhi_pci *nhi_pci = nhi_to_pci(nhi);
|
||||
struct pci_dev *pdev = to_pci_dev(nhi->dev);
|
||||
struct device *dev = &pdev->dev;
|
||||
int res, irq, nvec;
|
||||
|
||||
ida_init(&nhi_pci->msix_ida);
|
||||
@@ -139,7 +138,7 @@ static int nhi_pci_init_msi(struct tb_nhi *nhi)
|
||||
res = devm_request_irq(&pdev->dev, irq, nhi_msi,
|
||||
IRQF_NO_SUSPEND, "thunderbolt", nhi);
|
||||
if (res)
|
||||
return dev_err_probe(dev, res, "request_irq failed, aborting\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -230,7 +229,7 @@ static void nhi_pci_ring_release_msix(struct tb_ring *ring)
|
||||
ring->irq = 0;
|
||||
}
|
||||
|
||||
static void nhi_pci_shutdown(struct tb_nhi *nhi)
|
||||
static void nhi_pci_release_irq(struct tb_nhi *nhi)
|
||||
{
|
||||
struct tb_nhi_pci *nhi_pci = nhi_to_pci(nhi);
|
||||
struct pci_dev *pdev = to_pci_dev(nhi->dev);
|
||||
@@ -256,7 +255,7 @@ static const struct tb_nhi_ops pci_nhi_default_ops = {
|
||||
.post_nvm_auth = nhi_pci_complete_dma_port,
|
||||
.request_ring_irq = nhi_pci_ring_request_msix,
|
||||
.release_ring_irq = nhi_pci_ring_release_msix,
|
||||
.shutdown = nhi_pci_shutdown,
|
||||
.shutdown = nhi_pci_release_irq,
|
||||
.is_present = nhi_pci_is_present,
|
||||
.init_interrupts = nhi_pci_init_msi,
|
||||
};
|
||||
@@ -424,7 +423,7 @@ static int icl_nhi_resume(struct tb_nhi *nhi)
|
||||
|
||||
static void icl_nhi_shutdown(struct tb_nhi *nhi)
|
||||
{
|
||||
nhi_pci_shutdown(nhi);
|
||||
nhi_pci_release_irq(nhi);
|
||||
|
||||
icl_nhi_force_power(nhi, false);
|
||||
}
|
||||
@@ -479,11 +478,19 @@ static int nhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
return nhi_probe(&nhi_pci->nhi);
|
||||
}
|
||||
|
||||
static void nhi_pci_remove(struct pci_dev *pdev)
|
||||
static void nhi_pci_do_remove(struct pci_dev *pdev, bool reset)
|
||||
{
|
||||
struct tb *tb = pci_get_drvdata(pdev);
|
||||
struct tb_nhi *nhi = tb->nhi;
|
||||
|
||||
/*
|
||||
* On system shutdown/reboot force a host router reset so the
|
||||
* connection manager asserts DPR on connected Thunderbolt 3 devices
|
||||
* before the router tree is removed (see tb_stop()).
|
||||
*/
|
||||
if (reset)
|
||||
nhi->host_reset = true;
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_forbid(&pdev->dev);
|
||||
@@ -493,6 +500,16 @@ static void nhi_pci_remove(struct pci_dev *pdev)
|
||||
nhi_shutdown(nhi);
|
||||
}
|
||||
|
||||
static void nhi_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
nhi_pci_do_remove(pdev, false);
|
||||
}
|
||||
|
||||
static void nhi_pci_shutdown(struct pci_dev *pdev)
|
||||
{
|
||||
nhi_pci_do_remove(pdev, true);
|
||||
}
|
||||
|
||||
static struct pci_device_id nhi_ids[] = {
|
||||
/*
|
||||
* We have to specify class, the TB bridges use the same device and
|
||||
@@ -593,7 +610,7 @@ static struct pci_driver nhi_driver = {
|
||||
.id_table = nhi_ids,
|
||||
.probe = nhi_pci_probe,
|
||||
.remove = nhi_pci_remove,
|
||||
.shutdown = nhi_pci_remove,
|
||||
.shutdown = nhi_pci_shutdown,
|
||||
.driver.pm = &nhi_pm_ops,
|
||||
};
|
||||
|
||||
|
||||
@@ -1540,7 +1540,7 @@ static void tbstream_group_detach_stream(struct tbstream *stream)
|
||||
config_group_put(&sg->group);
|
||||
}
|
||||
|
||||
static int tbstream_probe(struct tb_service *svc, const struct tb_service_id *id)
|
||||
static int tbstream_probe(struct tb_service *svc)
|
||||
{
|
||||
struct tbstream *stream;
|
||||
|
||||
@@ -1630,7 +1630,7 @@ static const struct dev_pm_ops tbstream_pm_ops = {
|
||||
|
||||
static const struct tb_service_id tbstream_ids[] = {
|
||||
{ TB_SERVICE("stream", 1) },
|
||||
{ },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(tbsvc, tbstream_ids);
|
||||
|
||||
|
||||
@@ -682,7 +682,16 @@ int tb_port_disable(struct tb_port *port)
|
||||
return __tb_port_enable(port, false);
|
||||
}
|
||||
|
||||
static int tb_port_reset(struct tb_port *port)
|
||||
/**
|
||||
* tb_port_reset() - Reset the port
|
||||
* @port: Port to reset
|
||||
*
|
||||
* Resets @port. For USB4 ports this issues a USB4 port reset and for
|
||||
* legacy ports the link controller port is reset.
|
||||
*
|
||||
* Return: %0 on success, negative errno otherwise.
|
||||
*/
|
||||
int tb_port_reset(struct tb_port *port)
|
||||
{
|
||||
if (tb_switch_is_usb4(port->sw))
|
||||
return port->cap_usb4 ? usb4_port_reset(port) : 0;
|
||||
|
||||
@@ -2941,7 +2941,9 @@ 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_nhi *nhi = tb->nhi;
|
||||
struct tb_tunnel *tunnel;
|
||||
struct tb_port *port;
|
||||
struct tb_tunnel *n;
|
||||
|
||||
cancel_delayed_work(&tcm->remove_work);
|
||||
@@ -2956,6 +2958,25 @@ static void tb_stop(struct tb *tb)
|
||||
tb_tunnel_deactivate(tunnel);
|
||||
tb_tunnel_put(tunnel);
|
||||
}
|
||||
/*
|
||||
* Signal disconnect to connected devices before the router tree is
|
||||
* removed below. A Thunderbolt 3 device directly connected to a USB4
|
||||
* host otherwise never receives a disconnect indication, leaving
|
||||
* firmware to poll the dead link for up to ~60 s which on some
|
||||
* platforms turns the shutdown into a warm reset. Asserting
|
||||
* PORT_CS_19.DPR drives SBTX low (USB4 spec section 6.9) so the device
|
||||
* detects SBRX low and goes to Uninitialized Unplugged immediately.
|
||||
*/
|
||||
if (nhi->host_reset) {
|
||||
tb_switch_for_each_port(tb->root_switch, port) {
|
||||
if (!tb_port_is_null(port) || !tb_port_has_remote(port))
|
||||
continue;
|
||||
if (tb_switch_is_usb4(port->remote->sw))
|
||||
continue;
|
||||
if (tb_port_reset(port))
|
||||
tb_port_dbg(port, "downstream port reset failed, continuing\n");
|
||||
}
|
||||
}
|
||||
tb_switch_remove(tb->root_switch);
|
||||
tb->root_switch = NULL;
|
||||
tcm->hotplug_active = false; /* signal tb_handle_hotplug to quit */
|
||||
|
||||
@@ -1103,6 +1103,7 @@ int tb_port_clear_counter(struct tb_port *port, int counter);
|
||||
int tb_port_unlock(struct tb_port *port);
|
||||
int tb_port_enable(struct tb_port *port);
|
||||
int tb_port_disable(struct tb_port *port);
|
||||
int tb_port_reset(struct tb_port *port);
|
||||
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);
|
||||
|
||||
@@ -968,6 +968,9 @@ tb_xdp_schedule_request(struct tb *tb, const struct tb_xdp_header *hdr,
|
||||
*/
|
||||
int tb_register_service_driver(struct tb_service_driver *drv)
|
||||
{
|
||||
if (!drv->probe)
|
||||
return -EINVAL;
|
||||
|
||||
drv->driver.bus = &tb_bus_type;
|
||||
return driver_register(&drv->driver);
|
||||
}
|
||||
@@ -1811,6 +1814,7 @@ static void tb_xdomain_state_work(struct work_struct *work)
|
||||
tb_xdomain_failed(xd);
|
||||
} else {
|
||||
xd->state = XDOMAIN_STATE_ENUMERATED;
|
||||
tb_xdomain_queue_properties_changed(xd);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -465,7 +465,7 @@ static inline struct tb_service *tb_to_service(struct device *dev)
|
||||
*/
|
||||
struct tb_service_driver {
|
||||
struct device_driver driver;
|
||||
int (*probe)(struct tb_service *svc, const struct tb_service_id *id);
|
||||
int (*probe)(struct tb_service *svc);
|
||||
void (*remove)(struct tb_service *svc);
|
||||
void (*shutdown)(struct tb_service *svc);
|
||||
const struct tb_service_id *id_table;
|
||||
@@ -514,6 +514,11 @@ void tb_service_properties_changed(struct tb_service *svc);
|
||||
* @hop_count: Number of rings (end point hops) supported by NHI.
|
||||
* @quirks: NHI specific quirks if any
|
||||
* @domain_released: Completed when domain has been fully released
|
||||
* @host_reset: Host router was reset on driver load, or forced on system
|
||||
* shutdown/reboot. When set, tb_stop() asserts DPR on connected
|
||||
* downstream ports to signal disconnect before tearing down the
|
||||
* router tree. Only Thunderbolt 3 devices are reset; USB4
|
||||
* routers are skipped.
|
||||
*/
|
||||
struct tb_nhi {
|
||||
spinlock_t lock;
|
||||
@@ -528,6 +533,7 @@ struct tb_nhi {
|
||||
u32 hop_count;
|
||||
unsigned long quirks;
|
||||
struct completion domain_released;
|
||||
bool host_reset;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user