mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-09 06:43:09 +00:00
thunderbolt: Changes for v5.15 merge window
This includes following Thunderbolt/USB4 changes for the v5.15 merge window: * Include authorized value in the KOBJ_CHANGE event of a device router * A couple of improvements to get the driver working also with the AMD USB4 host controller. All these have been in linux-next with no reported issues. -----BEGIN PGP SIGNATURE----- iQJUBAABCgA+FiEEVTdhRGBbNzLrSUBaAP2fSd+ZWKAFAmEkzz8gHG1pa2Eud2Vz dGVyYmVyZ0BsaW51eC5pbnRlbC5jb20ACgkQAP2fSd+ZWKCU1hAAiyvLKqFBsgye y8Zp6aeKkIjZ8wqnM0q97ba+cq23T/NwGsgSVDPLJesZc2hJPbAQIf0Iy3BKFjL3 G5feynsWxLhHqnOsY3tL7iw1lUJoreeagPycZ5Bj4xkVDEsCbvyGnDCrlLsO4MzP Ia9zFN6kHyLGEmk0DROM1lAWZiAf7gIL6XW+mU2KDCYYaTcgQE2Im199yr3oqgi/ 8y4VsYkHolg3r0DtiwKBa5jFZ+iiCJJqAjz8+Yh12bjB0+dSBlxJWSpeH/fsDPwq FqMX6lMZuQPd71dlBOAIFr8RHjEg0owX8HUyxVb9MLoufhLFf0MIS/uKYRRfxTIH zcTf8LOmZIM8FUqtsnXT3nO7rdCz2YWUmmjPLkYX2Ssi1XoxWvkeWBfKmZyXHiKB uWwaHc3+uLSJSMvMqSLCTG6pK24Ux8C0LELoun4cj6CVi7018yLp+JiYAIn/QSoI QFIXg9EKC2WOXo539LxlkNVDnLumVhc3e/Sg+ykq8KVj++AfMbZzaOpvyhuDs9cj PHfSybRpC0+Q6RUMhazxjiMs2u8uRYkfp4kS6htg1MrxGcEbvv/22y0TuKl3D/nr pBEZGELgvnMd69TuhFP9yM4504P6BFaoQCDy9XhReeUTbRnmBBMDG5uSAoj/+gbe od3B0YhSIjujKBewMeXh689+30DYtqQ= =1bPR -----END PGP SIGNATURE----- Merge tag 'thunderbolt-for-v5.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt into usb-next Mika writes: thunderbolt: Changes for v5.15 merge window This includes following Thunderbolt/USB4 changes for the v5.15 merge window: * Include authorized value in the KOBJ_CHANGE event of a device router * A couple of improvements to get the driver working also with the AMD USB4 host controller. All these have been in linux-next with no reported issues. * tag 'thunderbolt-for-v5.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt: thunderbolt: Fix port linking by checking all adapters thunderbolt: Do not read control adapter config space thunderbolt: Handle ring interrupt by reading interrupt status register thunderbolt: Add vendor specific NHI quirk for auto-clearing interrupt status thunderbolt: Add authorized value to the KOBJ_CHANGE uevent
This commit is contained in:
commit
bfa109d761
@ -35,6 +35,8 @@
|
||||
|
||||
#define NHI_MAILBOX_TIMEOUT 500 /* ms */
|
||||
|
||||
#define QUIRK_AUTO_CLEAR_INT BIT(0)
|
||||
|
||||
static int ring_interrupt_index(struct tb_ring *ring)
|
||||
{
|
||||
int bit = ring->hop;
|
||||
@ -66,14 +68,17 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active)
|
||||
else
|
||||
index = ring->hop + ring->nhi->hop_count;
|
||||
|
||||
/*
|
||||
* Ask the hardware to clear interrupt status bits automatically
|
||||
* since we already know which interrupt was triggered.
|
||||
*/
|
||||
misc = ioread32(ring->nhi->iobase + REG_DMA_MISC);
|
||||
if (!(misc & REG_DMA_MISC_INT_AUTO_CLEAR)) {
|
||||
misc |= REG_DMA_MISC_INT_AUTO_CLEAR;
|
||||
iowrite32(misc, ring->nhi->iobase + REG_DMA_MISC);
|
||||
if (ring->nhi->quirks & QUIRK_AUTO_CLEAR_INT) {
|
||||
/*
|
||||
* Ask the hardware to clear interrupt status
|
||||
* bits automatically since we already know
|
||||
* which interrupt was triggered.
|
||||
*/
|
||||
misc = ioread32(ring->nhi->iobase + REG_DMA_MISC);
|
||||
if (!(misc & REG_DMA_MISC_INT_AUTO_CLEAR)) {
|
||||
misc |= REG_DMA_MISC_INT_AUTO_CLEAR;
|
||||
iowrite32(misc, ring->nhi->iobase + REG_DMA_MISC);
|
||||
}
|
||||
}
|
||||
|
||||
ivr_base = ring->nhi->iobase + REG_INT_VEC_ALLOC_BASE;
|
||||
@ -377,11 +382,24 @@ void tb_ring_poll_complete(struct tb_ring *ring)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tb_ring_poll_complete);
|
||||
|
||||
static void ring_clear_msix(const struct tb_ring *ring)
|
||||
{
|
||||
if (ring->nhi->quirks & QUIRK_AUTO_CLEAR_INT)
|
||||
return;
|
||||
|
||||
if (ring->is_tx)
|
||||
ioread32(ring->nhi->iobase + REG_RING_NOTIFY_BASE);
|
||||
else
|
||||
ioread32(ring->nhi->iobase + REG_RING_NOTIFY_BASE +
|
||||
4 * (ring->nhi->hop_count / 32));
|
||||
}
|
||||
|
||||
static irqreturn_t ring_msix(int irq, void *data)
|
||||
{
|
||||
struct tb_ring *ring = data;
|
||||
|
||||
spin_lock(&ring->nhi->lock);
|
||||
ring_clear_msix(ring);
|
||||
spin_lock(&ring->lock);
|
||||
__ring_interrupt(ring);
|
||||
spin_unlock(&ring->lock);
|
||||
@ -1074,6 +1092,16 @@ static void nhi_shutdown(struct tb_nhi *nhi)
|
||||
nhi->ops->shutdown(nhi);
|
||||
}
|
||||
|
||||
static void nhi_check_quirks(struct tb_nhi *nhi)
|
||||
{
|
||||
/*
|
||||
* Intel hardware supports auto clear of the interrupt status
|
||||
* reqister right after interrupt is being issued.
|
||||
*/
|
||||
if (nhi->pdev->vendor == PCI_VENDOR_ID_INTEL)
|
||||
nhi->quirks |= QUIRK_AUTO_CLEAR_INT;
|
||||
}
|
||||
|
||||
static int nhi_init_msi(struct tb_nhi *nhi)
|
||||
{
|
||||
struct pci_dev *pdev = nhi->pdev;
|
||||
@ -1190,6 +1218,8 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
if (!nhi->tx_rings || !nhi->rx_rings)
|
||||
return -ENOMEM;
|
||||
|
||||
nhi_check_quirks(nhi);
|
||||
|
||||
res = nhi_init_msi(nhi);
|
||||
if (res) {
|
||||
dev_err(&pdev->dev, "cannot enable MSI, aborting\n");
|
||||
|
@ -724,6 +724,12 @@ static int tb_init_port(struct tb_port *port)
|
||||
int res;
|
||||
int cap;
|
||||
|
||||
INIT_LIST_HEAD(&port->list);
|
||||
|
||||
/* Control adapter does not have configuration space */
|
||||
if (!port->port)
|
||||
return 0;
|
||||
|
||||
res = tb_port_read(port, &port->config, TB_CFG_PORT, 0, 8);
|
||||
if (res) {
|
||||
if (res == -ENODEV) {
|
||||
@ -736,7 +742,7 @@ static int tb_init_port(struct tb_port *port)
|
||||
}
|
||||
|
||||
/* Port 0 is the switch itself and has no PHY. */
|
||||
if (port->config.type == TB_TYPE_PORT && port->port != 0) {
|
||||
if (port->config.type == TB_TYPE_PORT) {
|
||||
cap = tb_port_find_cap(port, TB_PORT_CAP_PHY);
|
||||
|
||||
if (cap > 0)
|
||||
@ -762,7 +768,7 @@ static int tb_init_port(struct tb_port *port)
|
||||
if (!port->ctl_credits)
|
||||
port->ctl_credits = 2;
|
||||
|
||||
} else if (port->port != 0) {
|
||||
} else {
|
||||
cap = tb_port_find_cap(port, TB_PORT_CAP_ADAP);
|
||||
if (cap > 0)
|
||||
port->cap_adap = cap;
|
||||
@ -773,10 +779,7 @@ static int tb_init_port(struct tb_port *port)
|
||||
ADP_CS_4_TOTAL_BUFFERS_SHIFT;
|
||||
|
||||
tb_dump_port(port->sw->tb, port);
|
||||
|
||||
INIT_LIST_HEAD(&port->list);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int tb_port_alloc_hopid(struct tb_port *port, bool in, int min_hopid,
|
||||
@ -1498,6 +1501,7 @@ static ssize_t authorized_show(struct device *dev,
|
||||
|
||||
static int disapprove_switch(struct device *dev, void *not_used)
|
||||
{
|
||||
char *envp[] = { "AUTHORIZED=0", NULL };
|
||||
struct tb_switch *sw;
|
||||
|
||||
sw = tb_to_switch(dev);
|
||||
@ -1514,7 +1518,7 @@ static int disapprove_switch(struct device *dev, void *not_used)
|
||||
return ret;
|
||||
|
||||
sw->authorized = 0;
|
||||
kobject_uevent(&sw->dev.kobj, KOBJ_CHANGE);
|
||||
kobject_uevent_env(&sw->dev.kobj, KOBJ_CHANGE, envp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1522,7 +1526,9 @@ static int disapprove_switch(struct device *dev, void *not_used)
|
||||
|
||||
static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val)
|
||||
{
|
||||
char envp_string[13];
|
||||
int ret = -EINVAL;
|
||||
char *envp[] = { envp_string, NULL };
|
||||
|
||||
if (!mutex_trylock(&sw->tb->lock))
|
||||
return restart_syscall();
|
||||
@ -1559,8 +1565,12 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val)
|
||||
|
||||
if (!ret) {
|
||||
sw->authorized = val;
|
||||
/* Notify status change to the userspace */
|
||||
kobject_uevent(&sw->dev.kobj, KOBJ_CHANGE);
|
||||
/*
|
||||
* Notify status change to the userspace, informing the new
|
||||
* value of /sys/bus/thunderbolt/devices/.../authorized.
|
||||
*/
|
||||
sprintf(envp_string, "AUTHORIZED=%u", sw->authorized);
|
||||
kobject_uevent_env(&sw->dev.kobj, KOBJ_CHANGE, envp);
|
||||
}
|
||||
|
||||
unlock:
|
||||
@ -2443,7 +2453,7 @@ static void tb_switch_default_link_ports(struct tb_switch *sw)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 1; i <= sw->config.max_port_number; i += 2) {
|
||||
for (i = 1; i <= sw->config.max_port_number; i++) {
|
||||
struct tb_port *port = &sw->ports[i];
|
||||
struct tb_port *subordinate;
|
||||
|
||||
|
@ -468,6 +468,7 @@ static inline struct tb_xdomain *tb_service_parent(struct tb_service *svc)
|
||||
* @interrupt_work: Work scheduled to handle ring interrupt when no
|
||||
* MSI-X is used.
|
||||
* @hop_count: Number of rings (end point hops) supported by NHI.
|
||||
* @quirks: NHI specific quirks if any
|
||||
*/
|
||||
struct tb_nhi {
|
||||
spinlock_t lock;
|
||||
@ -480,6 +481,7 @@ struct tb_nhi {
|
||||
bool going_away;
|
||||
struct work_struct interrupt_work;
|
||||
u32 hop_count;
|
||||
unsigned long quirks;
|
||||
};
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user