Char/Misc driver fixes for 5.2-rc6

Here are a number of small driver fixes for 5.2-rc6
 
 Nothing major, just fixes for reported issues:
   - soundwire fixes
   - thunderbolt fixes
   - MAINTAINERS update for fpga maintainer change
   - binder bugfix
   - habanalabs 64bit pointer fix
   - documentation updates
 
 All of these have been in linux-next with no reported issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCXQyOkg8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+yljIgCfbK0RWqIOa4esqlpUi2+71yM89M8AmQFLdOck
 gYJS7F+8cXeGEsveqAn2
 =4ujv
 -----END PGP SIGNATURE-----

Merge tag 'char-misc-5.2-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc

Pull char/misc driver fixes from Greg KH:
 "Here are a number of small driver fixes for 5.2-rc6

  Nothing major, just fixes for reported issues:
   - soundwire fixes
   - thunderbolt fixes
   - MAINTAINERS update for fpga maintainer change
   - binder bugfix
   - habanalabs 64bit pointer fix
   - documentation updates

  All of these have been in linux-next with no reported issues"

* tag 'char-misc-5.2-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc:
  habanalabs: use u64_to_user_ptr() for reading user pointers
  doc: fix documentation about UIO_MEM_LOGICAL using
  MAINTAINERS / Documentation: Thorsten Scherer is the successor of Gavin Schenk
  docs: fb: Add TER16x32 to the available font names
  MAINTAINERS: fpga: hand off maintainership to Moritz
  thunderbolt: Implement CIO reset correctly for Titan Ridge
  binder: fix possible UAF when freeing buffer
  thunderbolt: Make sure device runtime resume completes before taking domain lock
  soundwire: intel: set dai min and max channels correctly
  soundwire: stream: fix bad unlock balance
  soundwire: stream: fix out of boundary access on port properties
This commit is contained in:
Linus Torvalds 2019-06-21 10:18:16 -07:00
commit b7b8a44f3a
11 changed files with 202 additions and 98 deletions

View File

@ -1,6 +1,6 @@
What: /sys/bus/siox/devices/siox-X/active What: /sys/bus/siox/devices/siox-X/active
KernelVersion: 4.16 KernelVersion: 4.16
Contact: Gavin Schenk <g.schenk@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Contact: Thorsten Scherer <t.scherer@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Description: Description:
On reading represents the current state of the bus. If it On reading represents the current state of the bus. If it
contains a "0" the bus is stopped and connected devices are 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 What: /sys/bus/siox/devices/siox-X/device_add
KernelVersion: 4.16 KernelVersion: 4.16
Contact: Gavin Schenk <g.schenk@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Contact: Thorsten Scherer <t.scherer@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Description: Description:
Write-only file. Write Write-only file. Write
@ -27,13 +27,13 @@ Description:
What: /sys/bus/siox/devices/siox-X/device_remove What: /sys/bus/siox/devices/siox-X/device_remove
KernelVersion: 4.16 KernelVersion: 4.16
Contact: Gavin Schenk <g.schenk@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Contact: Thorsten Scherer <t.scherer@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Description: Description:
Write-only file. A single write removes the last device in the siox chain. Write-only file. A single write removes the last device in the siox chain.
What: /sys/bus/siox/devices/siox-X/poll_interval_ns What: /sys/bus/siox/devices/siox-X/poll_interval_ns
KernelVersion: 4.16 KernelVersion: 4.16
Contact: Gavin Schenk <g.schenk@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Contact: Thorsten Scherer <t.scherer@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Description: Description:
Defines the interval between two poll cycles in nano seconds. Defines the interval between two poll cycles in nano seconds.
Note this is rounded to jiffies on writing. On reading the current value 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 What: /sys/bus/siox/devices/siox-X-Y/connected
KernelVersion: 4.16 KernelVersion: 4.16
Contact: Gavin Schenk <g.schenk@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Contact: Thorsten Scherer <t.scherer@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Description: Description:
Read-only value. "0" means the Yth device on siox bus X isn't "connected" i.e. 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. communication with it is not ensured. "1" signals a working connection.
What: /sys/bus/siox/devices/siox-X-Y/inbytes What: /sys/bus/siox/devices/siox-X-Y/inbytes
KernelVersion: 4.16 KernelVersion: 4.16
Contact: Gavin Schenk <g.schenk@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Contact: Thorsten Scherer <t.scherer@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Description: Description:
Read-only value reporting the inbytes value provided to siox-X/device_add Read-only value reporting the inbytes value provided to siox-X/device_add
What: /sys/bus/siox/devices/siox-X-Y/status_errors What: /sys/bus/siox/devices/siox-X-Y/status_errors
KernelVersion: 4.16 KernelVersion: 4.16
Contact: Gavin Schenk <g.schenk@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Contact: Thorsten Scherer <t.scherer@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Description: Description:
Counts the number of time intervals when the read status byte doesn't yield the Counts the number of time intervals when the read status byte doesn't yield the
expected value. expected value.
What: /sys/bus/siox/devices/siox-X-Y/type What: /sys/bus/siox/devices/siox-X-Y/type
KernelVersion: 4.16 KernelVersion: 4.16
Contact: Gavin Schenk <g.schenk@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Contact: Thorsten Scherer <t.scherer@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Description: Description:
Read-only value reporting the type value provided to siox-X/device_add. Read-only value reporting the type value provided to siox-X/device_add.
What: /sys/bus/siox/devices/siox-X-Y/watchdog What: /sys/bus/siox/devices/siox-X-Y/watchdog
KernelVersion: 4.16 KernelVersion: 4.16
Contact: Gavin Schenk <g.schenk@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Contact: Thorsten Scherer <t.scherer@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Description: Description:
Read-only value reporting if the watchdog of the siox device is 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 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 What: /sys/bus/siox/devices/siox-X-Y/watchdog_errors
KernelVersion: 4.16 KernelVersion: 4.16
Contact: Gavin Schenk <g.schenk@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Contact: Thorsten Scherer <t.scherer@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Description: Description:
Read-only value reporting the number to time intervals when the Read-only value reporting the number to time intervals when the
watchdog was active. watchdog was active.
What: /sys/bus/siox/devices/siox-X-Y/outbytes What: /sys/bus/siox/devices/siox-X-Y/outbytes
KernelVersion: 4.16 KernelVersion: 4.16
Contact: Gavin Schenk <g.schenk@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Contact: Thorsten Scherer <t.scherer@eckelmann.de>, Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Description: Description:
Read-only value reporting the outbytes value provided to siox-X/device_add. Read-only value reporting the outbytes value provided to siox-X/device_add.

View File

@ -276,8 +276,8 @@ fields of ``struct uio_mem``:
- ``int memtype``: Required if the mapping is used. Set this to - ``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 ``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 mapped. Use ``UIO_MEM_LOGICAL`` for logical memory (e.g. allocated
with :c:func:`kmalloc()`). There's also ``UIO_MEM_VIRTUAL`` for with :c:func:`__get_free_pages()` but not kmalloc()). There's also
virtual memory. ``UIO_MEM_VIRTUAL`` for virtual memory.
- ``phys_addr_t addr``: Required if the mapping is used. Fill in the - ``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 address of your memory block. This address is the one that appears in

View File

@ -79,7 +79,7 @@ C. Boot options
Select the initial font to use. The value 'name' can be any of the Select the initial font to use. The value 'name' can be any of the
compiled-in fonts: 10x18, 6x10, 7x14, Acorn8x8, MINI4x6, 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, Note, not all drivers can handle font with widths not divisible by 8,
such as vga16fb. such as vga16fb.

View File

@ -6234,7 +6234,6 @@ F: include/linux/ipmi-fru.h
K: fmc_d.*register K: fmc_d.*register
FPGA MANAGER FRAMEWORK FPGA MANAGER FRAMEWORK
M: Alan Tull <atull@kernel.org>
M: Moritz Fischer <mdf@kernel.org> M: Moritz Fischer <mdf@kernel.org>
L: linux-fpga@vger.kernel.org L: linux-fpga@vger.kernel.org
S: Maintained S: Maintained
@ -14404,7 +14403,7 @@ F: lib/test_siphash.c
F: include/linux/siphash.h F: include/linux/siphash.h
SIOX SIOX
M: Gavin Schenk <g.schenk@eckelmann.de> M: Thorsten Scherer <t.scherer@eckelmann.de>
M: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> M: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
R: Pengutronix Kernel Team <kernel@pengutronix.de> R: Pengutronix Kernel Team <kernel@pengutronix.de>
S: Supported S: Supported

View File

@ -1941,8 +1941,18 @@ static void binder_free_txn_fixups(struct binder_transaction *t)
static void binder_free_transaction(struct binder_transaction *t) static void binder_free_transaction(struct binder_transaction *t)
{ {
struct binder_proc *target_proc = t->to_proc;
if (target_proc) {
binder_inner_proc_lock(target_proc);
if (t->buffer) if (t->buffer)
t->buffer->transaction = NULL; 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); binder_free_txn_fixups(t);
kfree(t); kfree(t);
binder_stats_deleted(BINDER_STAT_TRANSACTION); binder_stats_deleted(BINDER_STAT_TRANSACTION);
@ -3551,10 +3561,12 @@ err_invalid_target_handle:
static void static void
binder_free_buf(struct binder_proc *proc, struct binder_buffer *buffer) binder_free_buf(struct binder_proc *proc, struct binder_buffer *buffer)
{ {
binder_inner_proc_lock(proc);
if (buffer->transaction) { if (buffer->transaction) {
buffer->transaction->buffer = NULL; buffer->transaction->buffer = NULL;
buffer->transaction = NULL; buffer->transaction = NULL;
} }
binder_inner_proc_unlock(proc);
if (buffer->async_transaction && buffer->target_node) { if (buffer->async_transaction && buffer->target_node) {
struct binder_node *buf_node; struct binder_node *buf_node;
struct binder_work *w; struct binder_work *w;

View File

@ -140,7 +140,7 @@ static int debug_coresight(struct hl_device *hdev, struct hl_debug_args *args)
params->op = args->op; params->op = args->op;
if (args->input_ptr && args->input_size) { 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); args->input_size);
if (IS_ERR(input)) { if (IS_ERR(input)) {
rc = PTR_ERR(input); rc = PTR_ERR(input);

View File

@ -715,8 +715,8 @@ static int intel_create_dai(struct sdw_cdns *cdns,
return -ENOMEM; return -ENOMEM;
} }
dais[i].playback.channels_min = 1; dais[i].capture.channels_min = 1;
dais[i].playback.channels_max = max_ch; dais[i].capture.channels_max = max_ch;
dais[i].capture.rates = SNDRV_PCM_RATE_48000; dais[i].capture.rates = SNDRV_PCM_RATE_48000;
dais[i].capture.formats = SNDRV_PCM_FMTBIT_S16_LE; dais[i].capture.formats = SNDRV_PCM_FMTBIT_S16_LE;
} }

View File

@ -814,6 +814,7 @@ static int do_bank_switch(struct sdw_stream_runtime *stream)
goto error; goto error;
} }
if (bus->multi_link)
mutex_unlock(&bus->msg_lock); mutex_unlock(&bus->msg_lock);
} }
@ -1406,9 +1407,7 @@ struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave,
} }
for (i = 0; i < num_ports; i++) { for (i = 0; i < num_ports; i++) {
dpn_prop = &dpn_prop[i]; if (dpn_prop[i].num == port_num)
if (dpn_prop->num == port_num)
return &dpn_prop[i]; return &dpn_prop[i];
} }

View File

@ -56,6 +56,7 @@
* @max_boot_acl: Maximum number of preboot ACL entries (%0 if not supported) * @max_boot_acl: Maximum number of preboot ACL entries (%0 if not supported)
* @rpm: Does the controller support runtime PM (RTD3) * @rpm: Does the controller support runtime PM (RTD3)
* @is_supported: Checks if we can support ICM on this controller * @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_mode: Read and return the ICM firmware mode (optional)
* @get_route: Find a route string for given switch * @get_route: Find a route string for given switch
* @save_devices: Ask ICM to save devices to ACL when suspending (optional) * @save_devices: Ask ICM to save devices to ACL when suspending (optional)
@ -74,6 +75,7 @@ struct icm {
bool safe_mode; bool safe_mode;
bool rpm; bool rpm;
bool (*is_supported)(struct tb *tb); bool (*is_supported)(struct tb *tb);
int (*cio_reset)(struct tb *tb);
int (*get_mode)(struct tb *tb); int (*get_mode)(struct tb *tb);
int (*get_route)(struct tb *tb, u8 link, u8 depth, u64 *route); int (*get_route)(struct tb *tb, u8 link, u8 depth, u64 *route);
void (*save_devices)(struct tb *tb); 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; 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, static bool icm_match(const struct tb_cfg_request *req,
const struct ctl_pkg *pkg) const struct ctl_pkg *pkg)
{ {
@ -484,6 +545,7 @@ static void add_switch(struct tb_switch *parent_sw, u64 route,
sw->authorized = authorized; sw->authorized = authorized;
sw->security_level = security_level; sw->security_level = security_level;
sw->boot = boot; sw->boot = boot;
init_completion(&sw->rpm_complete);
vss = parse_intel_vss(ep_name, ep_name_size); vss = parse_intel_vss(ep_name, ep_name_size);
if (vss) if (vss)
@ -523,6 +585,9 @@ static void update_switch(struct tb_switch *parent_sw, struct tb_switch *sw,
/* This switch still exists */ /* This switch still exists */
sw->is_unplugged = false; sw->is_unplugged = false;
/* Runtime resume is now complete */
complete(&sw->rpm_complete);
} }
static void remove_switch(struct tb_switch *sw) 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 static int
icm_tr_driver_ready(struct tb *tb, enum tb_security_level *security_level, icm_tr_driver_ready(struct tb *tb, enum tb_security_level *security_level,
size_t *nboot_acl, bool *rpm) size_t *nboot_acl, bool *rpm)
@ -1240,6 +1310,11 @@ static bool icm_ar_is_supported(struct tb *tb)
return false; 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) static int icm_ar_get_mode(struct tb *tb)
{ {
struct tb_nhi *nhi = tb->nhi; struct tb_nhi *nhi = tb->nhi;
@ -1477,65 +1552,6 @@ __icm_driver_ready(struct tb *tb, enum tb_security_level *security_level,
return -ETIMEDOUT; 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) static int icm_firmware_reset(struct tb *tb, struct tb_nhi *nhi)
{ {
struct icm *icm = tb_priv(tb); 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); iowrite32(val, nhi->iobase + REG_FW_STS);
/* Trigger CIO reset now */ /* 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) 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) static void icm_free_unplugged_children(struct tb_switch *sw)
{ {
unsigned int i; unsigned int i;
@ -1782,7 +1824,7 @@ static void icm_free_unplugged_children(struct tb_switch *sw)
port->xdomain = NULL; port->xdomain = NULL;
} else if (tb_port_has_remote(port)) { } else if (tb_port_has_remote(port)) {
if (port->remote->sw->is_unplugged) { if (port->remote->sw->is_unplugged) {
tb_switch_remove(port->remote->sw); remove_unplugged_switch(port->remote->sw);
port->remote = NULL; port->remote = NULL;
} else { } else {
icm_free_unplugged_children(port->remote->sw); icm_free_unplugged_children(port->remote->sw);
@ -1831,6 +1873,24 @@ static int icm_runtime_suspend(struct tb *tb)
return 0; 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) static int icm_runtime_resume(struct tb *tb)
{ {
/* /*
@ -1910,6 +1970,8 @@ static const struct tb_cm_ops icm_ar_ops = {
.complete = icm_complete, .complete = icm_complete,
.runtime_suspend = icm_runtime_suspend, .runtime_suspend = icm_runtime_suspend,
.runtime_resume = icm_runtime_resume, .runtime_resume = icm_runtime_resume,
.runtime_suspend_switch = icm_runtime_suspend_switch,
.runtime_resume_switch = icm_runtime_resume_switch,
.handle_event = icm_handle_event, .handle_event = icm_handle_event,
.get_boot_acl = icm_ar_get_boot_acl, .get_boot_acl = icm_ar_get_boot_acl,
.set_boot_acl = icm_ar_set_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, .complete = icm_complete,
.runtime_suspend = icm_runtime_suspend, .runtime_suspend = icm_runtime_suspend,
.runtime_resume = icm_runtime_resume, .runtime_resume = icm_runtime_resume,
.runtime_suspend_switch = icm_runtime_suspend_switch,
.runtime_resume_switch = icm_runtime_resume_switch,
.handle_event = icm_handle_event, .handle_event = icm_handle_event,
.get_boot_acl = icm_ar_get_boot_acl, .get_boot_acl = icm_ar_get_boot_acl,
.set_boot_acl = icm_ar_set_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: case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_NHI:
icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES; icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES;
icm->is_supported = icm_ar_is_supported; icm->is_supported = icm_ar_is_supported;
icm->cio_reset = icm_ar_cio_reset;
icm->get_mode = icm_ar_get_mode; icm->get_mode = icm_ar_get_mode;
icm->get_route = icm_ar_get_route; icm->get_route = icm_ar_get_route;
icm->save_devices = icm_fr_save_devices; 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: case PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_NHI:
icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES; icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES;
icm->is_supported = icm_ar_is_supported; icm->is_supported = icm_ar_is_supported;
icm->cio_reset = icm_tr_cio_reset;
icm->get_mode = icm_ar_get_mode; icm->get_mode = icm_ar_get_mode;
icm->driver_ready = icm_tr_driver_ready; icm->driver_ready = icm_tr_driver_ready;
icm->device_connected = icm_tr_device_connected; icm->device_connected = icm_tr_device_connected;

View File

@ -239,7 +239,16 @@ static int tb_switch_nvm_read(void *priv, unsigned int offset, void *val,
int ret; int ret;
pm_runtime_get_sync(&sw->dev); 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); 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_mark_last_busy(&sw->dev);
pm_runtime_put_autosuspend(&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. * the new tunnel too early.
*/ */
pci_lock_rescan_remove(); pci_lock_rescan_remove();
pm_runtime_get_sync(&sw->dev);
switch (val) { switch (val) {
/* Approve switch */ /* Approve switch */
@ -1040,8 +1048,6 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val)
break; break;
} }
pm_runtime_mark_last_busy(&sw->dev);
pm_runtime_put_autosuspend(&sw->dev);
pci_unlock_rescan_remove(); pci_unlock_rescan_remove();
if (!ret) { if (!ret) {
@ -1069,7 +1075,10 @@ static ssize_t authorized_store(struct device *dev,
if (val > 2) if (val > 2)
return -EINVAL; return -EINVAL;
pm_runtime_get_sync(&sw->dev);
ret = tb_switch_set_authorized(sw, val); ret = tb_switch_set_authorized(sw, val);
pm_runtime_mark_last_busy(&sw->dev);
pm_runtime_put_autosuspend(&sw->dev);
return ret ? ret : count; return ret ? ret : count;
} }
@ -1195,8 +1204,12 @@ static ssize_t nvm_authenticate_store(struct device *dev,
bool val; bool val;
int ret; int ret;
if (!mutex_trylock(&sw->tb->lock)) pm_runtime_get_sync(&sw->dev);
return restart_syscall();
if (!mutex_trylock(&sw->tb->lock)) {
ret = restart_syscall();
goto exit_rpm;
}
/* If NVMem devices are not yet added */ /* If NVMem devices are not yet added */
if (!sw->nvm) { if (!sw->nvm) {
@ -1217,13 +1230,9 @@ static ssize_t nvm_authenticate_store(struct device *dev,
goto exit_unlock; goto exit_unlock;
} }
pm_runtime_get_sync(&sw->dev);
ret = nvm_validate_and_write(sw); ret = nvm_validate_and_write(sw);
if (ret) { if (ret)
pm_runtime_mark_last_busy(&sw->dev);
pm_runtime_put_autosuspend(&sw->dev);
goto exit_unlock; goto exit_unlock;
}
sw->nvm->authenticating = true; sw->nvm->authenticating = true;
@ -1239,12 +1248,13 @@ static ssize_t nvm_authenticate_store(struct device *dev,
} else { } else {
ret = nvm_authenticate_device(sw); ret = nvm_authenticate_device(sw);
} }
pm_runtime_mark_last_busy(&sw->dev);
pm_runtime_put_autosuspend(&sw->dev);
} }
exit_unlock: exit_unlock:
mutex_unlock(&sw->tb->lock); mutex_unlock(&sw->tb->lock);
exit_rpm:
pm_runtime_mark_last_busy(&sw->dev);
pm_runtime_put_autosuspend(&sw->dev);
if (ret) if (ret)
return 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) 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; return 0;
} }
static int __maybe_unused tb_switch_runtime_resume(struct device *dev) 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; return 0;
} }

View File

@ -79,6 +79,8 @@ struct tb_switch_nvm {
* @connection_key: Connection key used with ICM messaging * @connection_key: Connection key used with ICM messaging
* @link: Root switch link this switch is connected (ICM only) * @link: Root switch link this switch is connected (ICM only)
* @depth: Depth in the chain 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 * When the switch is being added or removed to the domain (other
* switches) you need to have domain lock held. * switches) you need to have domain lock held.
@ -112,6 +114,7 @@ struct tb_switch {
u8 connection_key; u8 connection_key;
u8 link; u8 link;
u8 depth; u8 depth;
struct completion rpm_complete;
}; };
/** /**
@ -250,6 +253,8 @@ struct tb_path {
* @complete: Connection manager specific complete * @complete: Connection manager specific complete
* @runtime_suspend: Connection manager specific runtime_suspend * @runtime_suspend: Connection manager specific runtime_suspend
* @runtime_resume: Connection manager specific runtime_resume * @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 * @handle_event: Handle thunderbolt event
* @get_boot_acl: Get boot ACL list * @get_boot_acl: Get boot ACL list
* @set_boot_acl: Set boot ACL list * @set_boot_acl: Set boot ACL list
@ -270,6 +275,8 @@ struct tb_cm_ops {
void (*complete)(struct tb *tb); void (*complete)(struct tb *tb);
int (*runtime_suspend)(struct tb *tb); int (*runtime_suspend)(struct tb *tb);
int (*runtime_resume)(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, void (*handle_event)(struct tb *tb, enum tb_cfg_pkg_type,
const void *buf, size_t size); const void *buf, size_t size);
int (*get_boot_acl)(struct tb *tb, uuid_t *uuids, size_t nuuids); int (*get_boot_acl)(struct tb *tb, uuid_t *uuids, size_t nuuids);