diff --git a/Documentation/ABI/testing/sysfs-bus-siox b/Documentation/ABI/testing/sysfs-bus-siox index fed7c3765a4e..c2a403f20b90 100644 --- a/Documentation/ABI/testing/sysfs-bus-siox +++ b/Documentation/ABI/testing/sysfs-bus-siox @@ -1,6 +1,6 @@ What: /sys/bus/siox/devices/siox-X/active KernelVersion: 4.16 -Contact: Gavin Schenk , Uwe Kleine-König +Contact: Thorsten Scherer , Uwe Kleine-König Description: On reading represents the current state of the bus. If it contains a "0" the bus is stopped and connected devices are @@ -12,7 +12,7 @@ Description: What: /sys/bus/siox/devices/siox-X/device_add KernelVersion: 4.16 -Contact: Gavin Schenk , Uwe Kleine-König +Contact: Thorsten Scherer , Uwe Kleine-König Description: Write-only file. Write @@ -27,13 +27,13 @@ Description: What: /sys/bus/siox/devices/siox-X/device_remove KernelVersion: 4.16 -Contact: Gavin Schenk , Uwe Kleine-König +Contact: Thorsten Scherer , Uwe Kleine-König Description: Write-only file. A single write removes the last device in the siox chain. What: /sys/bus/siox/devices/siox-X/poll_interval_ns KernelVersion: 4.16 -Contact: Gavin Schenk , Uwe Kleine-König +Contact: Thorsten Scherer , Uwe Kleine-König Description: Defines the interval between two poll cycles in nano seconds. Note this is rounded to jiffies on writing. On reading the current value @@ -41,33 +41,33 @@ Description: What: /sys/bus/siox/devices/siox-X-Y/connected KernelVersion: 4.16 -Contact: Gavin Schenk , Uwe Kleine-König +Contact: Thorsten Scherer , Uwe Kleine-König Description: Read-only value. "0" means the Yth device on siox bus X isn't "connected" i.e. communication with it is not ensured. "1" signals a working connection. What: /sys/bus/siox/devices/siox-X-Y/inbytes KernelVersion: 4.16 -Contact: Gavin Schenk , Uwe Kleine-König +Contact: Thorsten Scherer , Uwe Kleine-König Description: Read-only value reporting the inbytes value provided to siox-X/device_add What: /sys/bus/siox/devices/siox-X-Y/status_errors KernelVersion: 4.16 -Contact: Gavin Schenk , Uwe Kleine-König +Contact: Thorsten Scherer , Uwe Kleine-König Description: Counts the number of time intervals when the read status byte doesn't yield the expected value. What: /sys/bus/siox/devices/siox-X-Y/type KernelVersion: 4.16 -Contact: Gavin Schenk , Uwe Kleine-König +Contact: Thorsten Scherer , Uwe Kleine-König Description: Read-only value reporting the type value provided to siox-X/device_add. What: /sys/bus/siox/devices/siox-X-Y/watchdog KernelVersion: 4.16 -Contact: Gavin Schenk , Uwe Kleine-König +Contact: Thorsten Scherer , Uwe Kleine-König Description: Read-only value reporting if the watchdog of the siox device is active. "0" means the watchdog is not active and the device is expected to @@ -75,13 +75,13 @@ Description: What: /sys/bus/siox/devices/siox-X-Y/watchdog_errors KernelVersion: 4.16 -Contact: Gavin Schenk , Uwe Kleine-König +Contact: Thorsten Scherer , Uwe Kleine-König Description: Read-only value reporting the number to time intervals when the watchdog was active. What: /sys/bus/siox/devices/siox-X-Y/outbytes KernelVersion: 4.16 -Contact: Gavin Schenk , Uwe Kleine-König +Contact: Thorsten Scherer , Uwe Kleine-König Description: Read-only value reporting the outbytes value provided to siox-X/device_add. diff --git a/Documentation/driver-api/uio-howto.rst b/Documentation/driver-api/uio-howto.rst index 25f50eace28b..8fecfa11d4ff 100644 --- a/Documentation/driver-api/uio-howto.rst +++ b/Documentation/driver-api/uio-howto.rst @@ -276,8 +276,8 @@ fields of ``struct uio_mem``: - ``int memtype``: Required if the mapping is used. Set this to ``UIO_MEM_PHYS`` if you you have physical memory on your card to be mapped. Use ``UIO_MEM_LOGICAL`` for logical memory (e.g. allocated - with :c:func:`kmalloc()`). There's also ``UIO_MEM_VIRTUAL`` for - virtual memory. + with :c:func:`__get_free_pages()` but not kmalloc()). There's also + ``UIO_MEM_VIRTUAL`` for virtual memory. - ``phys_addr_t addr``: Required if the mapping is used. Fill in the address of your memory block. This address is the one that appears in diff --git a/Documentation/fb/fbcon.txt b/Documentation/fb/fbcon.txt index 60a5ec04e8f0..5a865437b33f 100644 --- a/Documentation/fb/fbcon.txt +++ b/Documentation/fb/fbcon.txt @@ -79,7 +79,7 @@ C. Boot options Select the initial font to use. The value 'name' can be any of the compiled-in fonts: 10x18, 6x10, 7x14, Acorn8x8, MINI4x6, - PEARL8x8, ProFont6x11, SUN12x22, SUN8x16, VGA8x16, VGA8x8. + PEARL8x8, ProFont6x11, SUN12x22, SUN8x16, TER16x32, VGA8x16, VGA8x8. Note, not all drivers can handle font with widths not divisible by 8, such as vga16fb. diff --git a/MAINTAINERS b/MAINTAINERS index 46d3497af51f..d0ed735994a5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6234,7 +6234,6 @@ F: include/linux/ipmi-fru.h K: fmc_d.*register FPGA MANAGER FRAMEWORK -M: Alan Tull M: Moritz Fischer L: linux-fpga@vger.kernel.org S: Maintained @@ -14404,7 +14403,7 @@ F: lib/test_siphash.c F: include/linux/siphash.h SIOX -M: Gavin Schenk +M: Thorsten Scherer M: Uwe Kleine-König R: Pengutronix Kernel Team S: Supported diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 748ac489ef7e..bc26b5511f0a 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -1941,8 +1941,18 @@ static void binder_free_txn_fixups(struct binder_transaction *t) static void binder_free_transaction(struct binder_transaction *t) { - if (t->buffer) - t->buffer->transaction = NULL; + struct binder_proc *target_proc = t->to_proc; + + if (target_proc) { + binder_inner_proc_lock(target_proc); + if (t->buffer) + t->buffer->transaction = NULL; + binder_inner_proc_unlock(target_proc); + } + /* + * If the transaction has no target_proc, then + * t->buffer->transaction has already been cleared. + */ binder_free_txn_fixups(t); kfree(t); binder_stats_deleted(BINDER_STAT_TRANSACTION); @@ -3551,10 +3561,12 @@ static void binder_transaction(struct binder_proc *proc, static void binder_free_buf(struct binder_proc *proc, struct binder_buffer *buffer) { + binder_inner_proc_lock(proc); if (buffer->transaction) { buffer->transaction->buffer = NULL; buffer->transaction = NULL; } + binder_inner_proc_unlock(proc); if (buffer->async_transaction && buffer->target_node) { struct binder_node *buf_node; struct binder_work *w; diff --git a/drivers/misc/habanalabs/habanalabs_ioctl.c b/drivers/misc/habanalabs/habanalabs_ioctl.c index eeefb22023e9..b7a0eecf6b6c 100644 --- a/drivers/misc/habanalabs/habanalabs_ioctl.c +++ b/drivers/misc/habanalabs/habanalabs_ioctl.c @@ -140,7 +140,7 @@ static int debug_coresight(struct hl_device *hdev, struct hl_debug_args *args) params->op = args->op; if (args->input_ptr && args->input_size) { - input = memdup_user((const void __user *) args->input_ptr, + input = memdup_user(u64_to_user_ptr(args->input_ptr), args->input_size); if (IS_ERR(input)) { rc = PTR_ERR(input); diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 31336b0271b0..60293a00a14e 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -715,8 +715,8 @@ static int intel_create_dai(struct sdw_cdns *cdns, return -ENOMEM; } - dais[i].playback.channels_min = 1; - dais[i].playback.channels_max = max_ch; + dais[i].capture.channels_min = 1; + dais[i].capture.channels_max = max_ch; dais[i].capture.rates = SNDRV_PCM_RATE_48000; dais[i].capture.formats = SNDRV_PCM_FMTBIT_S16_LE; } diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index d01060dbee96..1d5294b8783b 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -814,7 +814,8 @@ static int do_bank_switch(struct sdw_stream_runtime *stream) goto error; } - mutex_unlock(&bus->msg_lock); + if (bus->multi_link) + mutex_unlock(&bus->msg_lock); } return ret; @@ -1406,9 +1407,7 @@ struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave, } for (i = 0; i < num_ports; i++) { - dpn_prop = &dpn_prop[i]; - - if (dpn_prop->num == port_num) + if (dpn_prop[i].num == port_num) return &dpn_prop[i]; } diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index f1c10378fa3e..fbdcef56a676 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -56,6 +56,7 @@ * @max_boot_acl: Maximum number of preboot ACL entries (%0 if not supported) * @rpm: Does the controller support runtime PM (RTD3) * @is_supported: Checks if we can support ICM on this controller + * @cio_reset: Trigger CIO reset * @get_mode: Read and return the ICM firmware mode (optional) * @get_route: Find a route string for given switch * @save_devices: Ask ICM to save devices to ACL when suspending (optional) @@ -74,6 +75,7 @@ struct icm { bool safe_mode; bool rpm; bool (*is_supported)(struct tb *tb); + int (*cio_reset)(struct tb *tb); int (*get_mode)(struct tb *tb); int (*get_route)(struct tb *tb, u8 link, u8 depth, u64 *route); void (*save_devices)(struct tb *tb); @@ -166,6 +168,65 @@ static inline u64 get_parent_route(u64 route) return depth ? route & ~(0xffULL << (depth - 1) * TB_ROUTE_SHIFT) : 0; } +static int pci2cio_wait_completion(struct icm *icm, unsigned long timeout_msec) +{ + unsigned long end = jiffies + msecs_to_jiffies(timeout_msec); + u32 cmd; + + do { + pci_read_config_dword(icm->upstream_port, + icm->vnd_cap + PCIE2CIO_CMD, &cmd); + if (!(cmd & PCIE2CIO_CMD_START)) { + if (cmd & PCIE2CIO_CMD_TIMEOUT) + break; + return 0; + } + + msleep(50); + } while (time_before(jiffies, end)); + + return -ETIMEDOUT; +} + +static int pcie2cio_read(struct icm *icm, enum tb_cfg_space cs, + unsigned int port, unsigned int index, u32 *data) +{ + struct pci_dev *pdev = icm->upstream_port; + int ret, vnd_cap = icm->vnd_cap; + u32 cmd; + + cmd = index; + cmd |= (port << PCIE2CIO_CMD_PORT_SHIFT) & PCIE2CIO_CMD_PORT_MASK; + cmd |= (cs << PCIE2CIO_CMD_CS_SHIFT) & PCIE2CIO_CMD_CS_MASK; + cmd |= PCIE2CIO_CMD_START; + pci_write_config_dword(pdev, vnd_cap + PCIE2CIO_CMD, cmd); + + ret = pci2cio_wait_completion(icm, 5000); + if (ret) + return ret; + + pci_read_config_dword(pdev, vnd_cap + PCIE2CIO_RDDATA, data); + return 0; +} + +static int pcie2cio_write(struct icm *icm, enum tb_cfg_space cs, + unsigned int port, unsigned int index, u32 data) +{ + struct pci_dev *pdev = icm->upstream_port; + int vnd_cap = icm->vnd_cap; + u32 cmd; + + pci_write_config_dword(pdev, vnd_cap + PCIE2CIO_WRDATA, data); + + cmd = index; + cmd |= (port << PCIE2CIO_CMD_PORT_SHIFT) & PCIE2CIO_CMD_PORT_MASK; + cmd |= (cs << PCIE2CIO_CMD_CS_SHIFT) & PCIE2CIO_CMD_CS_MASK; + cmd |= PCIE2CIO_CMD_WRITE | PCIE2CIO_CMD_START; + pci_write_config_dword(pdev, vnd_cap + PCIE2CIO_CMD, cmd); + + return pci2cio_wait_completion(icm, 5000); +} + static bool icm_match(const struct tb_cfg_request *req, const struct ctl_pkg *pkg) { @@ -484,6 +545,7 @@ static void add_switch(struct tb_switch *parent_sw, u64 route, sw->authorized = authorized; sw->security_level = security_level; sw->boot = boot; + init_completion(&sw->rpm_complete); vss = parse_intel_vss(ep_name, ep_name_size); if (vss) @@ -523,6 +585,9 @@ static void update_switch(struct tb_switch *parent_sw, struct tb_switch *sw, /* This switch still exists */ sw->is_unplugged = false; + + /* Runtime resume is now complete */ + complete(&sw->rpm_complete); } static void remove_switch(struct tb_switch *sw) @@ -834,6 +899,11 @@ icm_fr_xdomain_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) } } +static int icm_tr_cio_reset(struct tb *tb) +{ + return pcie2cio_write(tb_priv(tb), TB_CFG_SWITCH, 0, 0x777, BIT(1)); +} + static int icm_tr_driver_ready(struct tb *tb, enum tb_security_level *security_level, size_t *nboot_acl, bool *rpm) @@ -1240,6 +1310,11 @@ static bool icm_ar_is_supported(struct tb *tb) return false; } +static int icm_ar_cio_reset(struct tb *tb) +{ + return pcie2cio_write(tb_priv(tb), TB_CFG_SWITCH, 0, 0x50, BIT(9)); +} + static int icm_ar_get_mode(struct tb *tb) { struct tb_nhi *nhi = tb->nhi; @@ -1477,65 +1552,6 @@ __icm_driver_ready(struct tb *tb, enum tb_security_level *security_level, return -ETIMEDOUT; } -static int pci2cio_wait_completion(struct icm *icm, unsigned long timeout_msec) -{ - unsigned long end = jiffies + msecs_to_jiffies(timeout_msec); - u32 cmd; - - do { - pci_read_config_dword(icm->upstream_port, - icm->vnd_cap + PCIE2CIO_CMD, &cmd); - if (!(cmd & PCIE2CIO_CMD_START)) { - if (cmd & PCIE2CIO_CMD_TIMEOUT) - break; - return 0; - } - - msleep(50); - } while (time_before(jiffies, end)); - - return -ETIMEDOUT; -} - -static int pcie2cio_read(struct icm *icm, enum tb_cfg_space cs, - unsigned int port, unsigned int index, u32 *data) -{ - struct pci_dev *pdev = icm->upstream_port; - int ret, vnd_cap = icm->vnd_cap; - u32 cmd; - - cmd = index; - cmd |= (port << PCIE2CIO_CMD_PORT_SHIFT) & PCIE2CIO_CMD_PORT_MASK; - cmd |= (cs << PCIE2CIO_CMD_CS_SHIFT) & PCIE2CIO_CMD_CS_MASK; - cmd |= PCIE2CIO_CMD_START; - pci_write_config_dword(pdev, vnd_cap + PCIE2CIO_CMD, cmd); - - ret = pci2cio_wait_completion(icm, 5000); - if (ret) - return ret; - - pci_read_config_dword(pdev, vnd_cap + PCIE2CIO_RDDATA, data); - return 0; -} - -static int pcie2cio_write(struct icm *icm, enum tb_cfg_space cs, - unsigned int port, unsigned int index, u32 data) -{ - struct pci_dev *pdev = icm->upstream_port; - int vnd_cap = icm->vnd_cap; - u32 cmd; - - pci_write_config_dword(pdev, vnd_cap + PCIE2CIO_WRDATA, data); - - cmd = index; - cmd |= (port << PCIE2CIO_CMD_PORT_SHIFT) & PCIE2CIO_CMD_PORT_MASK; - cmd |= (cs << PCIE2CIO_CMD_CS_SHIFT) & PCIE2CIO_CMD_CS_MASK; - cmd |= PCIE2CIO_CMD_WRITE | PCIE2CIO_CMD_START; - pci_write_config_dword(pdev, vnd_cap + PCIE2CIO_CMD, cmd); - - return pci2cio_wait_completion(icm, 5000); -} - static int icm_firmware_reset(struct tb *tb, struct tb_nhi *nhi) { struct icm *icm = tb_priv(tb); @@ -1556,7 +1572,7 @@ static int icm_firmware_reset(struct tb *tb, struct tb_nhi *nhi) iowrite32(val, nhi->iobase + REG_FW_STS); /* Trigger CIO reset now */ - return pcie2cio_write(icm, TB_CFG_SWITCH, 0, 0x50, BIT(9)); + return icm->cio_reset(tb); } static int icm_firmware_start(struct tb *tb, struct tb_nhi *nhi) @@ -1770,6 +1786,32 @@ static void icm_unplug_children(struct tb_switch *sw) } } +static int complete_rpm(struct device *dev, void *data) +{ + struct tb_switch *sw = tb_to_switch(dev); + + if (sw) + complete(&sw->rpm_complete); + return 0; +} + +static void remove_unplugged_switch(struct tb_switch *sw) +{ + pm_runtime_get_sync(sw->dev.parent); + + /* + * Signal this and switches below for rpm_complete because + * tb_switch_remove() calls pm_runtime_get_sync() that then waits + * for it. + */ + complete_rpm(&sw->dev, NULL); + bus_for_each_dev(&tb_bus_type, &sw->dev, NULL, complete_rpm); + tb_switch_remove(sw); + + pm_runtime_mark_last_busy(sw->dev.parent); + pm_runtime_put_autosuspend(sw->dev.parent); +} + static void icm_free_unplugged_children(struct tb_switch *sw) { unsigned int i; @@ -1782,7 +1824,7 @@ static void icm_free_unplugged_children(struct tb_switch *sw) port->xdomain = NULL; } else if (tb_port_has_remote(port)) { if (port->remote->sw->is_unplugged) { - tb_switch_remove(port->remote->sw); + remove_unplugged_switch(port->remote->sw); port->remote = NULL; } else { icm_free_unplugged_children(port->remote->sw); @@ -1831,6 +1873,24 @@ static int icm_runtime_suspend(struct tb *tb) return 0; } +static int icm_runtime_suspend_switch(struct tb_switch *sw) +{ + if (tb_route(sw)) + reinit_completion(&sw->rpm_complete); + return 0; +} + +static int icm_runtime_resume_switch(struct tb_switch *sw) +{ + if (tb_route(sw)) { + if (!wait_for_completion_timeout(&sw->rpm_complete, + msecs_to_jiffies(500))) { + dev_dbg(&sw->dev, "runtime resuming timed out\n"); + } + } + return 0; +} + static int icm_runtime_resume(struct tb *tb) { /* @@ -1910,6 +1970,8 @@ static const struct tb_cm_ops icm_ar_ops = { .complete = icm_complete, .runtime_suspend = icm_runtime_suspend, .runtime_resume = icm_runtime_resume, + .runtime_suspend_switch = icm_runtime_suspend_switch, + .runtime_resume_switch = icm_runtime_resume_switch, .handle_event = icm_handle_event, .get_boot_acl = icm_ar_get_boot_acl, .set_boot_acl = icm_ar_set_boot_acl, @@ -1930,6 +1992,8 @@ static const struct tb_cm_ops icm_tr_ops = { .complete = icm_complete, .runtime_suspend = icm_runtime_suspend, .runtime_resume = icm_runtime_resume, + .runtime_suspend_switch = icm_runtime_suspend_switch, + .runtime_resume_switch = icm_runtime_resume_switch, .handle_event = icm_handle_event, .get_boot_acl = icm_ar_get_boot_acl, .set_boot_acl = icm_ar_set_boot_acl, @@ -1975,6 +2039,7 @@ struct tb *icm_probe(struct tb_nhi *nhi) case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_NHI: icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES; icm->is_supported = icm_ar_is_supported; + icm->cio_reset = icm_ar_cio_reset; icm->get_mode = icm_ar_get_mode; icm->get_route = icm_ar_get_route; icm->save_devices = icm_fr_save_devices; @@ -1990,6 +2055,7 @@ struct tb *icm_probe(struct tb_nhi *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->cio_reset = icm_tr_cio_reset; icm->get_mode = icm_ar_get_mode; icm->driver_ready = icm_tr_driver_ready; icm->device_connected = icm_tr_device_connected; diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index c1b016574fb4..10b56c66fec3 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -239,7 +239,16 @@ static int tb_switch_nvm_read(void *priv, unsigned int offset, void *val, int ret; pm_runtime_get_sync(&sw->dev); + + if (!mutex_trylock(&sw->tb->lock)) { + ret = restart_syscall(); + goto out; + } + ret = dma_port_flash_read(sw->dma_port, offset, val, bytes); + mutex_unlock(&sw->tb->lock); + +out: pm_runtime_mark_last_busy(&sw->dev); pm_runtime_put_autosuspend(&sw->dev); @@ -1019,7 +1028,6 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val) * the new tunnel too early. */ pci_lock_rescan_remove(); - pm_runtime_get_sync(&sw->dev); switch (val) { /* Approve switch */ @@ -1040,8 +1048,6 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val) break; } - pm_runtime_mark_last_busy(&sw->dev); - pm_runtime_put_autosuspend(&sw->dev); pci_unlock_rescan_remove(); if (!ret) { @@ -1069,7 +1075,10 @@ static ssize_t authorized_store(struct device *dev, if (val > 2) return -EINVAL; + pm_runtime_get_sync(&sw->dev); ret = tb_switch_set_authorized(sw, val); + pm_runtime_mark_last_busy(&sw->dev); + pm_runtime_put_autosuspend(&sw->dev); return ret ? ret : count; } @@ -1195,8 +1204,12 @@ static ssize_t nvm_authenticate_store(struct device *dev, bool val; int ret; - if (!mutex_trylock(&sw->tb->lock)) - return restart_syscall(); + pm_runtime_get_sync(&sw->dev); + + if (!mutex_trylock(&sw->tb->lock)) { + ret = restart_syscall(); + goto exit_rpm; + } /* If NVMem devices are not yet added */ if (!sw->nvm) { @@ -1217,13 +1230,9 @@ static ssize_t nvm_authenticate_store(struct device *dev, goto exit_unlock; } - pm_runtime_get_sync(&sw->dev); ret = nvm_validate_and_write(sw); - if (ret) { - pm_runtime_mark_last_busy(&sw->dev); - pm_runtime_put_autosuspend(&sw->dev); + if (ret) goto exit_unlock; - } sw->nvm->authenticating = true; @@ -1239,12 +1248,13 @@ static ssize_t nvm_authenticate_store(struct device *dev, } else { ret = nvm_authenticate_device(sw); } - pm_runtime_mark_last_busy(&sw->dev); - pm_runtime_put_autosuspend(&sw->dev); } exit_unlock: mutex_unlock(&sw->tb->lock); +exit_rpm: + pm_runtime_mark_last_busy(&sw->dev); + pm_runtime_put_autosuspend(&sw->dev); if (ret) return ret; @@ -1380,11 +1390,22 @@ static void tb_switch_release(struct device *dev) */ static int __maybe_unused tb_switch_runtime_suspend(struct device *dev) { + struct tb_switch *sw = tb_to_switch(dev); + const struct tb_cm_ops *cm_ops = sw->tb->cm_ops; + + if (cm_ops->runtime_suspend_switch) + return cm_ops->runtime_suspend_switch(sw); + return 0; } static int __maybe_unused tb_switch_runtime_resume(struct device *dev) { + struct tb_switch *sw = tb_to_switch(dev); + const struct tb_cm_ops *cm_ops = sw->tb->cm_ops; + + if (cm_ops->runtime_resume_switch) + return cm_ops->runtime_resume_switch(sw); return 0; } diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index b12c8f33d89c..6407d529871d 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -79,6 +79,8 @@ struct tb_switch_nvm { * @connection_key: Connection key used with ICM messaging * @link: Root switch link this switch is connected (ICM only) * @depth: Depth in the chain this switch is connected (ICM only) + * @rpm_complete: Completion used to wait for runtime resume to + * complete (ICM only) * * When the switch is being added or removed to the domain (other * switches) you need to have domain lock held. @@ -112,6 +114,7 @@ struct tb_switch { u8 connection_key; u8 link; u8 depth; + struct completion rpm_complete; }; /** @@ -250,6 +253,8 @@ struct tb_path { * @complete: Connection manager specific complete * @runtime_suspend: Connection manager specific runtime_suspend * @runtime_resume: Connection manager specific runtime_resume + * @runtime_suspend_switch: Runtime suspend a switch + * @runtime_resume_switch: Runtime resume a switch * @handle_event: Handle thunderbolt event * @get_boot_acl: Get boot ACL list * @set_boot_acl: Set boot ACL list @@ -270,6 +275,8 @@ struct tb_cm_ops { void (*complete)(struct tb *tb); int (*runtime_suspend)(struct tb *tb); int (*runtime_resume)(struct tb *tb); + int (*runtime_suspend_switch)(struct tb_switch *sw); + int (*runtime_resume_switch)(struct tb_switch *sw); void (*handle_event)(struct tb *tb, enum tb_cfg_pkg_type, const void *buf, size_t size); int (*get_boot_acl)(struct tb *tb, uuid_t *uuids, size_t nuuids);