From 5220cb493bf418cc4ce5f3ba961dbd0207441731 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 30 Nov 2022 15:48:38 +0200 Subject: xhci: Convert to use list_count_nodes() The list API provides the list_count_nodes() to help with counting existing nodes in the list. Utilise it. Acked-by: Mathias Nyman Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20221130134838.23805-4-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index ddc30037f9ce..aa4d34efecd2 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2528,7 +2528,6 @@ static int handle_tx_event(struct xhci_hcd *xhci, union xhci_trb *ep_trb; int status = -EINPROGRESS; struct xhci_ep_ctx *ep_ctx; - struct list_head *tmp; u32 trb_comp_code; int td_num = 0; bool handling_skipped_tds = false; @@ -2582,10 +2581,8 @@ static int handle_tx_event(struct xhci_hcd *xhci, } /* Count current td numbers if ep->skip is set */ - if (ep->skip) { - list_for_each(tmp, &ep_ring->td_list) - td_num++; - } + if (ep->skip) + td_num += list_count_nodes(&ep_ring->td_list); /* Look for common error cases */ switch (trb_comp_code) { -- cgit v1.2.3 From ee0e40efc4d1bd87c795233c8ceadf4228479c06 Mon Sep 17 00:00:00 2001 From: Sing-Han Chen Date: Wed, 11 Jan 2023 11:04:50 +0000 Subject: usb: host: xhci-tegra: Add Tegra234 XHCI support This change adds Tegra234 XUSB host mode controller support. In Tegra234, some of the registers have moved to bar2 space. The new soc variable has_bar2 indicates the chip with bar2 area. This patch adds new reg helper to let the driver reuse the same code for those chips with bar2 support. Signed-off-by: Sing-Han Chen Co-developed-by: Wayne Chang Signed-off-by: Wayne Chang Signed-off-by: Jon Hunter Link: https://lore.kernel.org/r/20230111110450.24617-7-jonathanh@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-tegra.c | 267 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 228 insertions(+), 39 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index bdb776553826..3b1ef120d4ca 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -44,6 +44,9 @@ #define XUSB_CFG_4 0x010 #define XUSB_BASE_ADDR_SHIFT 15 #define XUSB_BASE_ADDR_MASK 0x1ffff +#define XUSB_CFG_7 0x01c +#define XUSB_BASE2_ADDR_SHIFT 16 +#define XUSB_BASE2_ADDR_MASK 0xffff #define XUSB_CFG_16 0x040 #define XUSB_CFG_24 0x060 #define XUSB_CFG_AXI_CFG 0x0f8 @@ -75,6 +78,20 @@ #define MBOX_SMI_INTR_FW_HANG BIT(1) #define MBOX_SMI_INTR_EN BIT(3) +/* BAR2 registers */ +#define XUSB_BAR2_ARU_MBOX_CMD 0x004 +#define XUSB_BAR2_ARU_MBOX_DATA_IN 0x008 +#define XUSB_BAR2_ARU_MBOX_DATA_OUT 0x00c +#define XUSB_BAR2_ARU_MBOX_OWNER 0x010 +#define XUSB_BAR2_ARU_SMI_INTR 0x014 +#define XUSB_BAR2_ARU_SMI_ARU_FW_SCRATCH_DATA0 0x01c +#define XUSB_BAR2_ARU_IFRDMA_CFG0 0x0e0 +#define XUSB_BAR2_ARU_IFRDMA_CFG1 0x0e4 +#define XUSB_BAR2_ARU_IFRDMA_STREAMID_FIELD 0x0e8 +#define XUSB_BAR2_ARU_C11_CSBRANGE 0x9c +#define XUSB_BAR2_ARU_FW_SCRATCH 0x1000 +#define XUSB_BAR2_CSB_BASE_ADDR 0x2000 + /* IPFS registers */ #define IPFS_XUSB_HOST_MSI_BAR_SZ_0 0x0c0 #define IPFS_XUSB_HOST_MSI_AXI_BAR_ST_0 0x0c4 @@ -111,6 +128,9 @@ #define IMFILLRNG1_TAG_HI_SHIFT 16 #define XUSB_FALC_IMFILLCTL 0x158 +/* CSB ARU registers */ +#define XUSB_CSB_ARU_SCRATCH0 0x100100 + /* MP CSB registers */ #define XUSB_CSB_MP_ILOAD_ATTR 0x101a00 #define XUSB_CSB_MP_ILOAD_BASE_LO 0x101a04 @@ -131,6 +151,9 @@ #define IMEM_BLOCK_SIZE 256 +#define FW_IOCTL_TYPE_SHIFT 24 +#define FW_IOCTL_CFGTBL_READ 17 + struct tegra_xusb_fw_header { __le32 boot_loadaddr_in_imem; __le32 boot_codedfi_offset; @@ -175,6 +198,7 @@ struct tegra_xusb_mbox_regs { u16 data_in; u16 data_out; u16 owner; + u16 smi_intr; }; struct tegra_xusb_context_soc { @@ -189,6 +213,14 @@ struct tegra_xusb_context_soc { } fpci; }; +struct tegra_xusb; +struct tegra_xusb_soc_ops { + u32 (*mbox_reg_readl)(struct tegra_xusb *tegra, unsigned int offset); + void (*mbox_reg_writel)(struct tegra_xusb *tegra, u32 value, unsigned int offset); + u32 (*csb_reg_readl)(struct tegra_xusb *tegra, unsigned int offset); + void (*csb_reg_writel)(struct tegra_xusb *tegra, u32 value, unsigned int offset); +}; + struct tegra_xusb_soc { const char *firmware; const char * const *supply_names; @@ -205,11 +237,14 @@ struct tegra_xusb_soc { } ports; struct tegra_xusb_mbox_regs mbox; + const struct tegra_xusb_soc_ops *ops; bool scale_ss_clock; bool has_ipfs; bool lpm_support; bool otg_reset_sspi; + + bool has_bar2; }; struct tegra_xusb_context { @@ -230,6 +265,8 @@ struct tegra_xusb { void __iomem *ipfs_base; void __iomem *fpci_base; + void __iomem *bar2_base; + struct resource *bar2; const struct tegra_xusb_soc *soc; @@ -300,7 +337,33 @@ static inline void ipfs_writel(struct tegra_xusb *tegra, u32 value, writel(value, tegra->ipfs_base + offset); } +static inline u32 bar2_readl(struct tegra_xusb *tegra, unsigned int offset) +{ + return readl(tegra->bar2_base + offset); +} + +static inline void bar2_writel(struct tegra_xusb *tegra, u32 value, + unsigned int offset) +{ + writel(value, tegra->bar2_base + offset); +} + static u32 csb_readl(struct tegra_xusb *tegra, unsigned int offset) +{ + const struct tegra_xusb_soc_ops *ops = tegra->soc->ops; + + return ops->csb_reg_readl(tegra, offset); +} + +static void csb_writel(struct tegra_xusb *tegra, u32 value, + unsigned int offset) +{ + const struct tegra_xusb_soc_ops *ops = tegra->soc->ops; + + ops->csb_reg_writel(tegra, value, offset); +} + +static u32 fpci_csb_readl(struct tegra_xusb *tegra, unsigned int offset) { u32 page = CSB_PAGE_SELECT(offset); u32 ofs = CSB_PAGE_OFFSET(offset); @@ -310,8 +373,8 @@ static u32 csb_readl(struct tegra_xusb *tegra, unsigned int offset) return fpci_readl(tegra, XUSB_CFG_CSB_BASE_ADDR + ofs); } -static void csb_writel(struct tegra_xusb *tegra, u32 value, - unsigned int offset) +static void fpci_csb_writel(struct tegra_xusb *tegra, u32 value, + unsigned int offset) { u32 page = CSB_PAGE_SELECT(offset); u32 ofs = CSB_PAGE_OFFSET(offset); @@ -320,6 +383,26 @@ static void csb_writel(struct tegra_xusb *tegra, u32 value, fpci_writel(tegra, value, XUSB_CFG_CSB_BASE_ADDR + ofs); } +static u32 bar2_csb_readl(struct tegra_xusb *tegra, unsigned int offset) +{ + u32 page = CSB_PAGE_SELECT(offset); + u32 ofs = CSB_PAGE_OFFSET(offset); + + bar2_writel(tegra, page, XUSB_BAR2_ARU_C11_CSBRANGE); + + return bar2_readl(tegra, XUSB_BAR2_CSB_BASE_ADDR + ofs); +} + +static void bar2_csb_writel(struct tegra_xusb *tegra, u32 value, + unsigned int offset) +{ + u32 page = CSB_PAGE_SELECT(offset); + u32 ofs = CSB_PAGE_OFFSET(offset); + + bar2_writel(tegra, page, XUSB_BAR2_ARU_C11_CSBRANGE); + bar2_writel(tegra, value, XUSB_BAR2_CSB_BASE_ADDR + ofs); +} + static int tegra_xusb_set_ss_clk(struct tegra_xusb *tegra, unsigned long rate) { @@ -451,6 +534,7 @@ static bool tegra_xusb_mbox_cmd_requires_ack(enum tegra_xusb_mbox_cmd cmd) static int tegra_xusb_mbox_send(struct tegra_xusb *tegra, const struct tegra_xusb_mbox_msg *msg) { + const struct tegra_xusb_soc_ops *ops = tegra->soc->ops; bool wait_for_idle = false; u32 value; @@ -459,15 +543,15 @@ static int tegra_xusb_mbox_send(struct tegra_xusb *tegra, * ACK/NAK messages. */ if (!(msg->cmd == MBOX_CMD_ACK || msg->cmd == MBOX_CMD_NAK)) { - value = fpci_readl(tegra, tegra->soc->mbox.owner); + value = ops->mbox_reg_readl(tegra, tegra->soc->mbox.owner); if (value != MBOX_OWNER_NONE) { dev_err(tegra->dev, "mailbox is busy\n"); return -EBUSY; } - fpci_writel(tegra, MBOX_OWNER_SW, tegra->soc->mbox.owner); + ops->mbox_reg_writel(tegra, MBOX_OWNER_SW, tegra->soc->mbox.owner); - value = fpci_readl(tegra, tegra->soc->mbox.owner); + value = ops->mbox_reg_readl(tegra, tegra->soc->mbox.owner); if (value != MBOX_OWNER_SW) { dev_err(tegra->dev, "failed to acquire mailbox\n"); return -EBUSY; @@ -477,17 +561,17 @@ static int tegra_xusb_mbox_send(struct tegra_xusb *tegra, } value = tegra_xusb_mbox_pack(msg); - fpci_writel(tegra, value, tegra->soc->mbox.data_in); + ops->mbox_reg_writel(tegra, value, tegra->soc->mbox.data_in); - value = fpci_readl(tegra, tegra->soc->mbox.cmd); + value = ops->mbox_reg_readl(tegra, tegra->soc->mbox.cmd); value |= MBOX_INT_EN | MBOX_DEST_FALC; - fpci_writel(tegra, value, tegra->soc->mbox.cmd); + ops->mbox_reg_writel(tegra, value, tegra->soc->mbox.cmd); if (wait_for_idle) { unsigned long timeout = jiffies + msecs_to_jiffies(250); while (time_before(jiffies, timeout)) { - value = fpci_readl(tegra, tegra->soc->mbox.owner); + value = ops->mbox_reg_readl(tegra, tegra->soc->mbox.owner); if (value == MBOX_OWNER_NONE) break; @@ -495,7 +579,7 @@ static int tegra_xusb_mbox_send(struct tegra_xusb *tegra, } if (time_after(jiffies, timeout)) - value = fpci_readl(tegra, tegra->soc->mbox.owner); + value = ops->mbox_reg_readl(tegra, tegra->soc->mbox.owner); if (value != MBOX_OWNER_NONE) return -ETIMEDOUT; @@ -507,11 +591,12 @@ static int tegra_xusb_mbox_send(struct tegra_xusb *tegra, static irqreturn_t tegra_xusb_mbox_irq(int irq, void *data) { struct tegra_xusb *tegra = data; + const struct tegra_xusb_soc_ops *ops = tegra->soc->ops; u32 value; /* clear mailbox interrupts */ - value = fpci_readl(tegra, XUSB_CFG_ARU_SMI_INTR); - fpci_writel(tegra, value, XUSB_CFG_ARU_SMI_INTR); + value = ops->mbox_reg_readl(tegra, tegra->soc->mbox.smi_intr); + ops->mbox_reg_writel(tegra, value, tegra->soc->mbox.smi_intr); if (value & MBOX_SMI_INTR_FW_HANG) dev_err(tegra->dev, "controller firmware hang\n"); @@ -664,6 +749,7 @@ static void tegra_xusb_mbox_handle(struct tegra_xusb *tegra, static irqreturn_t tegra_xusb_mbox_thread(int irq, void *data) { struct tegra_xusb *tegra = data; + const struct tegra_xusb_soc_ops *ops = tegra->soc->ops; struct tegra_xusb_mbox_msg msg; u32 value; @@ -672,16 +758,16 @@ static irqreturn_t tegra_xusb_mbox_thread(int irq, void *data) if (pm_runtime_suspended(tegra->dev) || tegra->suspended) goto out; - value = fpci_readl(tegra, tegra->soc->mbox.data_out); + value = ops->mbox_reg_readl(tegra, tegra->soc->mbox.data_out); tegra_xusb_mbox_unpack(&msg, value); - value = fpci_readl(tegra, tegra->soc->mbox.cmd); + value = ops->mbox_reg_readl(tegra, tegra->soc->mbox.cmd); value &= ~MBOX_DEST_SMI; - fpci_writel(tegra, value, tegra->soc->mbox.cmd); + ops->mbox_reg_writel(tegra, value, tegra->soc->mbox.cmd); /* clear mailbox owner if no ACK/NAK is required */ if (!tegra_xusb_mbox_cmd_requires_ack(msg.cmd)) - fpci_writel(tegra, MBOX_OWNER_NONE, tegra->soc->mbox.owner); + ops->mbox_reg_writel(tegra, MBOX_OWNER_NONE, tegra->soc->mbox.owner); tegra_xusb_mbox_handle(tegra, &msg); @@ -709,6 +795,15 @@ static void tegra_xusb_config(struct tegra_xusb *tegra) value |= regs & (XUSB_BASE_ADDR_MASK << XUSB_BASE_ADDR_SHIFT); fpci_writel(tegra, value, XUSB_CFG_4); + /* Program BAR2 space */ + if (tegra->bar2) { + value = fpci_readl(tegra, XUSB_CFG_7); + value &= ~(XUSB_BASE2_ADDR_MASK << XUSB_BASE2_ADDR_SHIFT); + value |= tegra->bar2->start & + (XUSB_BASE2_ADDR_MASK << XUSB_BASE2_ADDR_SHIFT); + fpci_writel(tegra, value, XUSB_CFG_7); + } + usleep_range(100, 200); /* Enable bus master */ @@ -881,21 +976,36 @@ static int tegra_xusb_request_firmware(struct tegra_xusb *tegra) return 0; } -static int tegra_xusb_load_firmware(struct tegra_xusb *tegra) +static int tegra_xusb_wait_for_falcon(struct tegra_xusb *tegra) +{ + struct xhci_cap_regs __iomem *cap_regs; + struct xhci_op_regs __iomem *op_regs; + int ret; + u32 value; + + cap_regs = tegra->regs; + op_regs = tegra->regs + HC_LENGTH(readl(&cap_regs->hc_capbase)); + + ret = readl_poll_timeout(&op_regs->status, value, !(value & STS_CNR), 1000, 200000); + + if (ret) + dev_err(tegra->dev, "XHCI Controller not ready. Falcon state: 0x%x\n", + csb_readl(tegra, XUSB_FALC_CPUCTL)); + + return ret; +} + +static int tegra_xusb_load_firmware_rom(struct tegra_xusb *tegra) { unsigned int code_tag_blocks, code_size_blocks, code_blocks; - struct xhci_cap_regs __iomem *cap = tegra->regs; struct tegra_xusb_fw_header *header; struct device *dev = tegra->dev; - struct xhci_op_regs __iomem *op; - unsigned long timeout; time64_t timestamp; u64 address; u32 value; int err; header = (struct tegra_xusb_fw_header *)tegra->fw.virt; - op = tegra->regs + HC_LENGTH(readl(&cap->hc_capbase)); if (csb_readl(tegra, XUSB_CSB_MP_ILOAD_BASE_LO) != 0) { dev_info(dev, "Firmware already loaded, Falcon state %#x\n", @@ -968,30 +1078,54 @@ static int tegra_xusb_load_firmware(struct tegra_xusb *tegra) /* Boot Falcon CPU and wait for USBSTS_CNR to get cleared. */ csb_writel(tegra, CPUCTL_STARTCPU, XUSB_FALC_CPUCTL); - timeout = jiffies + msecs_to_jiffies(200); + if (tegra_xusb_wait_for_falcon(tegra)) + return -EIO; + + timestamp = le32_to_cpu(header->fwimg_created_time); - do { - value = readl(&op->status); - if ((value & STS_CNR) == 0) - break; + dev_info(dev, "Firmware timestamp: %ptTs UTC\n", ×tamp); + + return 0; +} + +static u32 tegra_xusb_read_firmware_header(struct tegra_xusb *tegra, u32 offset) +{ + /* + * We only accept reading the firmware config table + * The offset should not exceed the fw header structure + */ + if (offset >= sizeof(struct tegra_xusb_fw_header)) + return 0; - usleep_range(1000, 2000); - } while (time_is_after_jiffies(timeout)); + bar2_writel(tegra, (FW_IOCTL_CFGTBL_READ << FW_IOCTL_TYPE_SHIFT) | offset, + XUSB_BAR2_ARU_FW_SCRATCH); + return bar2_readl(tegra, XUSB_BAR2_ARU_SMI_ARU_FW_SCRATCH_DATA0); +} + +static int tegra_xusb_init_ifr_firmware(struct tegra_xusb *tegra) +{ + time64_t timestamp; - value = readl(&op->status); - if (value & STS_CNR) { - value = csb_readl(tegra, XUSB_FALC_CPUCTL); - dev_err(dev, "XHCI controller not read: %#010x\n", value); + if (tegra_xusb_wait_for_falcon(tegra)) return -EIO; - } - timestamp = le32_to_cpu(header->fwimg_created_time); +#define offsetof_32(X, Y) ((u8)(offsetof(X, Y) / sizeof(__le32))) + timestamp = tegra_xusb_read_firmware_header(tegra, offsetof_32(struct tegra_xusb_fw_header, + fwimg_created_time) << 2); - dev_info(dev, "Firmware timestamp: %ptTs UTC\n", ×tamp); + dev_info(tegra->dev, "Firmware timestamp: %ptTs UTC\n", ×tamp); return 0; } +static int tegra_xusb_load_firmware(struct tegra_xusb *tegra) +{ + if (!tegra->soc->firmware) + return tegra_xusb_init_ifr_firmware(tegra); + else + return tegra_xusb_load_firmware_rom(tegra); +} + static void tegra_xusb_powerdomain_remove(struct device *dev, struct tegra_xusb *tegra) { @@ -1435,6 +1569,10 @@ static int tegra_xusb_probe(struct platform_device *pdev) tegra->ipfs_base = devm_platform_ioremap_resource(pdev, 2); if (IS_ERR(tegra->ipfs_base)) return PTR_ERR(tegra->ipfs_base); + } else if (tegra->soc->has_bar2) { + tegra->bar2_base = devm_platform_get_and_ioremap_resource(pdev, 2, &tegra->bar2); + if (IS_ERR(tegra->bar2_base)) + return PTR_ERR(tegra->bar2_base); } tegra->xhci_irq = platform_get_irq(pdev, 0); @@ -1651,10 +1789,13 @@ static int tegra_xusb_probe(struct platform_device *pdev) goto disable_phy; } - err = tegra_xusb_request_firmware(tegra); - if (err < 0) { - dev_err(&pdev->dev, "failed to request firmware: %d\n", err); - goto disable_phy; + if (tegra->soc->firmware) { + err = tegra_xusb_request_firmware(tegra); + if (err < 0) { + dev_err(&pdev->dev, + "failed to request firmware: %d\n", err); + goto disable_phy; + } } err = tegra_xusb_unpowergate_partitions(tegra); @@ -2271,6 +2412,13 @@ static const struct tegra_xusb_context_soc tegra124_xusb_context = { }, }; +static const struct tegra_xusb_soc_ops tegra124_ops = { + .mbox_reg_readl = &fpci_readl, + .mbox_reg_writel = &fpci_writel, + .csb_reg_readl = &fpci_csb_readl, + .csb_reg_writel = &fpci_csb_writel, +}; + static const struct tegra_xusb_soc tegra124_soc = { .firmware = "nvidia/tegra124/xusb.bin", .supply_names = tegra124_supply_names, @@ -2286,11 +2434,13 @@ static const struct tegra_xusb_soc tegra124_soc = { .scale_ss_clock = true, .has_ipfs = true, .otg_reset_sspi = false, + .ops = &tegra124_ops, .mbox = { .cmd = 0xe4, .data_in = 0xe8, .data_out = 0xec, .owner = 0xf0, + .smi_intr = XUSB_CFG_ARU_SMI_INTR, }, }; MODULE_FIRMWARE("nvidia/tegra124/xusb.bin"); @@ -2322,11 +2472,13 @@ static const struct tegra_xusb_soc tegra210_soc = { .scale_ss_clock = false, .has_ipfs = true, .otg_reset_sspi = true, + .ops = &tegra124_ops, .mbox = { .cmd = 0xe4, .data_in = 0xe8, .data_out = 0xec, .owner = 0xf0, + .smi_intr = XUSB_CFG_ARU_SMI_INTR, }, }; MODULE_FIRMWARE("nvidia/tegra210/xusb.bin"); @@ -2363,11 +2515,13 @@ static const struct tegra_xusb_soc tegra186_soc = { .scale_ss_clock = false, .has_ipfs = false, .otg_reset_sspi = false, + .ops = &tegra124_ops, .mbox = { .cmd = 0xe4, .data_in = 0xe8, .data_out = 0xec, .owner = 0xf0, + .smi_intr = XUSB_CFG_ARU_SMI_INTR, }, .lpm_support = true, }; @@ -2394,21 +2548,56 @@ static const struct tegra_xusb_soc tegra194_soc = { .scale_ss_clock = false, .has_ipfs = false, .otg_reset_sspi = false, + .ops = &tegra124_ops, .mbox = { .cmd = 0x68, .data_in = 0x6c, .data_out = 0x70, .owner = 0x74, + .smi_intr = XUSB_CFG_ARU_SMI_INTR, }, .lpm_support = true, }; MODULE_FIRMWARE("nvidia/tegra194/xusb.bin"); +static const struct tegra_xusb_soc_ops tegra234_ops = { + .mbox_reg_readl = &bar2_readl, + .mbox_reg_writel = &bar2_writel, + .csb_reg_readl = &bar2_csb_readl, + .csb_reg_writel = &bar2_csb_writel, +}; + +static const struct tegra_xusb_soc tegra234_soc = { + .supply_names = tegra194_supply_names, + .num_supplies = ARRAY_SIZE(tegra194_supply_names), + .phy_types = tegra194_phy_types, + .num_types = ARRAY_SIZE(tegra194_phy_types), + .context = &tegra186_xusb_context, + .ports = { + .usb3 = { .offset = 0, .count = 4, }, + .usb2 = { .offset = 4, .count = 4, }, + }, + .scale_ss_clock = false, + .has_ipfs = false, + .otg_reset_sspi = false, + .ops = &tegra234_ops, + .mbox = { + .cmd = XUSB_BAR2_ARU_MBOX_CMD, + .data_in = XUSB_BAR2_ARU_MBOX_DATA_IN, + .data_out = XUSB_BAR2_ARU_MBOX_DATA_OUT, + .owner = XUSB_BAR2_ARU_MBOX_OWNER, + .smi_intr = XUSB_BAR2_ARU_SMI_INTR, + }, + .lpm_support = true, + .has_bar2 = true, +}; + static const struct of_device_id tegra_xusb_of_match[] = { { .compatible = "nvidia,tegra124-xusb", .data = &tegra124_soc }, { .compatible = "nvidia,tegra210-xusb", .data = &tegra210_soc }, { .compatible = "nvidia,tegra186-xusb", .data = &tegra186_soc }, { .compatible = "nvidia,tegra194-xusb", .data = &tegra194_soc }, + { .compatible = "nvidia,tegra234-xusb", .data = &tegra234_soc }, { }, }; MODULE_DEVICE_TABLE(of, tegra_xusb_of_match); -- cgit v1.2.3 From 592338dde85411b1da74fa6b463e2a047c2545ab Mon Sep 17 00:00:00 2001 From: Jim Lin Date: Fri, 11 Nov 2022 18:18:11 +0800 Subject: xhci: Add hub_control to xhci_driver_overrides Add a hub_control() callback to the xhci_driver_overrides structure to allow host drivers to override the default hub_control function. This is required for Tegra which requires device specific actions for power management to be executed during USB state transitions. Signed-off-by: Jim Lin Reviewed-by: Jon Hunter Tested-by: Jon Hunter Acked-by: Thierry Reding Acked-by: Mathias Nyman Link: https://lore.kernel.org/r/20221111101813.32482-2-jilin@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 2 ++ drivers/usb/host/xhci.h | 2 ++ 2 files changed, 4 insertions(+) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 79d7931c048a..748c7158198f 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -5502,6 +5502,8 @@ void xhci_init_driver(struct hc_driver *drv, drv->check_bandwidth = over->check_bandwidth; if (over->reset_bandwidth) drv->reset_bandwidth = over->reset_bandwidth; + if (over->hub_control) + drv->hub_control = over->hub_control; } } EXPORT_SYMBOL_GPL(xhci_init_driver); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index c9f06c5e4e9d..f71841812f06 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1943,6 +1943,8 @@ struct xhci_driver_overrides { struct usb_host_endpoint *ep); int (*check_bandwidth)(struct usb_hcd *, struct usb_device *); void (*reset_bandwidth)(struct usb_hcd *, struct usb_device *); + int (*hub_control)(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength); }; #define XHCI_CFC_DELAY 10 -- cgit v1.2.3 From 2cbe475fe733a70eda30049ee8edf06f296c9402 Mon Sep 17 00:00:00 2001 From: Jim Lin Date: Fri, 11 Nov 2022 18:18:12 +0800 Subject: xhci: hub: export symbol on xhci_hub_control XHCI host drivers may override the default xhci_hub_control() with their own device specific function. To allow these host drivers to call the xhci_hub_control() function from within their own hub_control() callback and be built as a module, export the symbol for xhci_hub_control. Signed-off-by: Jim Lin Reviewed-by: Jon Hunter Tested-by: Jon Hunter Acked-by: Thierry Reding Acked-by: Mathias Nyman Link: https://lore.kernel.org/r/20221111101813.32482-3-jilin@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 94c94db3faf6..7750a5eed435 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -1623,6 +1623,7 @@ error: spin_unlock_irqrestore(&xhci->lock, flags); return retval; } +EXPORT_SYMBOL_GPL(xhci_hub_control); /* * Returns 0 if the status hasn't changed, or the number of bytes in buf. -- cgit v1.2.3 From a30951d31b250bf3479c00e93646b6cc6fb42a56 Mon Sep 17 00:00:00 2001 From: Petlozu Pravareshwar Date: Fri, 11 Nov 2022 18:18:13 +0800 Subject: xhci: tegra: USB2 pad power controls Program USB2 pad PD controls during port connect/disconnect, port suspend/resume, and test mode, to reduce power consumption on disconnect or suspend. Signed-off-by: Petlozu Pravareshwar Co-developed-by: Jim Lin Signed-off-by: Jim Lin Acked-by: Thierry Reding Reviewed-by: Jon Hunter Tested-by: Jon Hunter Acked-by: Mathias Nyman Link: https://lore.kernel.org/r/20221111101813.32482-4-jilin@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-tegra.c | 125 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 3b1ef120d4ca..1ff22f675930 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -311,6 +311,7 @@ struct tegra_xusb { bool suspended; struct tegra_xusb_context context; + u8 lp0_utmi_pad_mask; }; static struct hc_driver __read_mostly tegra_xhci_hc_driver; @@ -2092,10 +2093,24 @@ static void tegra_xhci_disable_phy_wake(struct tegra_xusb *tegra) struct tegra_xusb_padctl *padctl = tegra->padctl; unsigned int i; + for (i = 0; i < tegra->num_usb_phys; i++) { + struct phy *phy = tegra_xusb_get_phy(tegra, "usb2", i); + + if (!phy) + continue; + + if (tegra_xusb_padctl_remote_wake_detected(padctl, phy)) + tegra_phy_xusb_utmi_pad_power_on(phy); + } + for (i = 0; i < tegra->num_phys; i++) { if (!tegra->phys[i]) continue; + if (tegra_xusb_padctl_remote_wake_detected(padctl, tegra->phys[i])) + dev_dbg(tegra->dev, "%pOF remote wake detected\n", + tegra->phys[i]->dev.of_node); + tegra_xusb_padctl_disable_phy_wake(padctl, tegra->phys[i]); } } @@ -2113,6 +2128,28 @@ static void tegra_xhci_disable_phy_sleepwalk(struct tegra_xusb *tegra) } } +static void tegra_xhci_program_utmi_power_lp0_exit(struct tegra_xusb *tegra) +{ + unsigned int i, index_to_usb2; + struct phy *phy; + + for (i = 0; i < tegra->soc->num_types; i++) { + if (strcmp(tegra->soc->phy_types[i].name, "usb2") == 0) + index_to_usb2 = i; + } + + for (i = 0; i < tegra->num_usb_phys; i++) { + if (!is_host_mode_phy(tegra, index_to_usb2, i)) + continue; + + phy = tegra_xusb_get_phy(tegra, "usb2", i); + if (tegra->lp0_utmi_pad_mask & BIT(i)) + tegra_phy_xusb_utmi_pad_power_on(phy); + else + tegra_phy_xusb_utmi_pad_power_down(phy); + } +} + static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool runtime) { struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); @@ -2121,6 +2158,7 @@ static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool runtime) unsigned int i; int err; u32 usbcmd; + u32 portsc; dev_dbg(dev, "entering ELPG\n"); @@ -2134,6 +2172,15 @@ static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool runtime) goto out; } + for (i = 0; i < tegra->num_usb_phys; i++) { + if (!xhci->usb2_rhub.ports[i]) + continue; + portsc = readl(xhci->usb2_rhub.ports[i]->addr); + tegra->lp0_utmi_pad_mask &= ~BIT(i); + if (((portsc & PORT_PLS_MASK) == XDEV_U3) || ((portsc & DEV_SPEED_MASK) == XDEV_FS)) + tegra->lp0_utmi_pad_mask |= BIT(i); + } + err = xhci_suspend(xhci, wakeup); if (err < 0) { dev_err(tegra->dev, "failed to suspend XHCI: %d\n", err); @@ -2207,6 +2254,8 @@ static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool runtime) phy_power_on(tegra->phys[i]); } + if (tegra->suspended) + tegra_xhci_program_utmi_power_lp0_exit(tegra); tegra_xusb_config(tegra); tegra_xusb_restore_context(tegra); @@ -2626,8 +2675,84 @@ static int tegra_xhci_setup(struct usb_hcd *hcd) return xhci_gen_setup(hcd, tegra_xhci_quirks); } +static int tegra_xhci_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, u16 index, + char *buf, u16 length) +{ + struct tegra_xusb *tegra = dev_get_drvdata(hcd->self.controller); + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct xhci_hub *rhub; + struct xhci_bus_state *bus_state; + int port = (index & 0xff) - 1; + unsigned int i; + struct xhci_port **ports; + u32 portsc; + int ret; + struct phy *phy; + + rhub = &xhci->usb2_rhub; + bus_state = &rhub->bus_state; + if (bus_state->resuming_ports && hcd->speed == HCD_USB2) { + ports = rhub->ports; + i = rhub->num_ports; + while (i--) { + if (!test_bit(i, &bus_state->resuming_ports)) + continue; + portsc = readl(ports[i]->addr); + if ((portsc & PORT_PLS_MASK) == XDEV_RESUME) + tegra_phy_xusb_utmi_pad_power_on( + tegra_xusb_get_phy(tegra, "usb2", (int) i)); + } + } + + if (hcd->speed == HCD_USB2) { + phy = tegra_xusb_get_phy(tegra, "usb2", port); + if ((type_req == ClearPortFeature) && (value == USB_PORT_FEAT_SUSPEND)) { + if (!index || index > rhub->num_ports) + return -EPIPE; + tegra_phy_xusb_utmi_pad_power_on(phy); + } + if ((type_req == SetPortFeature) && (value == USB_PORT_FEAT_RESET)) { + if (!index || index > rhub->num_ports) + return -EPIPE; + ports = rhub->ports; + portsc = readl(ports[port]->addr); + if (portsc & PORT_CONNECT) + tegra_phy_xusb_utmi_pad_power_on(phy); + } + } + + ret = xhci_hub_control(hcd, type_req, value, index, buf, length); + if (ret < 0) + return ret; + + if (hcd->speed == HCD_USB2) { + /* Use phy where we set previously */ + if ((type_req == SetPortFeature) && (value == USB_PORT_FEAT_SUSPEND)) + /* We don't suspend the PAD while HNP role swap happens on the OTG port */ + if (!((hcd->self.otg_port == (port + 1)) && hcd->self.b_hnp_enable)) + tegra_phy_xusb_utmi_pad_power_down(phy); + + if ((type_req == ClearPortFeature) && (value == USB_PORT_FEAT_C_CONNECTION)) { + ports = rhub->ports; + portsc = readl(ports[port]->addr); + if (!(portsc & PORT_CONNECT)) { + /* We don't suspend the PAD while HNP role swap happens on the OTG + * port + */ + if (!((hcd->self.otg_port == (port + 1)) && hcd->self.b_hnp_enable)) + tegra_phy_xusb_utmi_pad_power_down(phy); + } + } + if ((type_req == SetPortFeature) && (value == USB_PORT_FEAT_TEST)) + tegra_phy_xusb_utmi_pad_power_on(phy); + } + + return ret; +} + static const struct xhci_driver_overrides tegra_xhci_overrides __initconst = { .reset = tegra_xhci_setup, + .hub_control = tegra_xhci_hub_control, }; static int __init tegra_xusb_init(void) -- cgit v1.2.3 From 4ba2e7cd986270dbb44eb693199a9d3dd3edf984 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Mon, 23 Jan 2023 11:00:07 +0100 Subject: usb: host: ehci-fsl: Use DRV_NAME "fsl-ehci" is used for both MODULE_ALIAS and driver name. As they have to match use DRV_NAME in both locations. Acked-by: Alan Stern Signed-off-by: Alexander Stein Link: https://lore.kernel.org/r/20230123100007.1479090-1-alexander.stein@ew.tq-group.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-fsl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 38d06e5abfbb..d74fa5ba845b 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -712,7 +712,7 @@ static struct platform_driver ehci_fsl_driver = { .remove = fsl_ehci_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { - .name = "fsl-ehci", + .name = DRV_NAME, .pm = EHCI_FSL_PM_OPS, }, }; -- cgit v1.2.3 From 8c6e8b09617915e8af3ab7dbfecd8f0a9c7cf94f Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sat, 21 Jan 2023 14:58:48 +0000 Subject: usb: host: xhci-plat: Improve clock handling in probe() It is always better to acquire all the clock resources first and then do the clock operations. This patch acquires all the optional clocks first and then calls corresponding prepare_enable(). There is no functional change. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20230121145853.4792-8-biju.das.jz@bp.renesas.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-plat.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 5fb55bf19493..11b3a0d6722d 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -257,16 +257,16 @@ static int xhci_plat_probe(struct platform_device *pdev) goto put_hcd; } - ret = clk_prepare_enable(xhci->reg_clk); - if (ret) - goto put_hcd; - xhci->clk = devm_clk_get_optional(&pdev->dev, NULL); if (IS_ERR(xhci->clk)) { ret = PTR_ERR(xhci->clk); - goto disable_reg_clk; + goto put_hcd; } + ret = clk_prepare_enable(xhci->reg_clk); + if (ret) + goto put_hcd; + ret = clk_prepare_enable(xhci->clk); if (ret) goto disable_reg_clk; -- cgit v1.2.3 From 224eb5311d6a8c180932465873d809b48a2470bf Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sat, 21 Jan 2023 14:58:49 +0000 Subject: usb: host: xhci-plat: Add reset support Add optional reset support. This is in preparation to adding USB xHCI support for RZ/V2M SoC. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20230121145853.4792-9-biju.das.jz@bp.renesas.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-plat.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 11b3a0d6722d..c5fc175a5fd1 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "xhci.h" #include "xhci-plat.h" @@ -263,10 +264,20 @@ static int xhci_plat_probe(struct platform_device *pdev) goto put_hcd; } - ret = clk_prepare_enable(xhci->reg_clk); + xhci->reset = devm_reset_control_array_get_optional_shared(&pdev->dev); + if (IS_ERR(xhci->reset)) { + ret = PTR_ERR(xhci->reset); + goto put_hcd; + } + + ret = reset_control_deassert(xhci->reset); if (ret) goto put_hcd; + ret = clk_prepare_enable(xhci->reg_clk); + if (ret) + goto err_reset; + ret = clk_prepare_enable(xhci->clk); if (ret) goto disable_reg_clk; @@ -377,6 +388,9 @@ disable_clk: disable_reg_clk: clk_disable_unprepare(xhci->reg_clk); +err_reset: + reset_control_assert(xhci->reset); + put_hcd: usb_put_hcd(hcd); @@ -412,6 +426,7 @@ static int xhci_plat_remove(struct platform_device *dev) clk_disable_unprepare(clk); clk_disable_unprepare(reg_clk); + reset_control_assert(xhci->reset); usb_put_hcd(hcd); pm_runtime_disable(&dev->dev); -- cgit v1.2.3 From c52c9acc415eb6ff54f658492f8c53da0fc3528a Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sat, 21 Jan 2023 14:58:50 +0000 Subject: xhci: host: Add Renesas RZ/V2M SoC support RZ/V2M is similar to R-Car XHCI but it doesn't require any firmware, we need to reset the USB Host reset release in DRD Module before accessing host registers. Signed-off-by: Biju Das Link: https://lore.kernel.org/r/20230121145853.4792-10-biju.das.jz@bp.renesas.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 10 ++++++++++ drivers/usb/host/Makefile | 3 +++ drivers/usb/host/xhci-plat.c | 11 +++++++++++ drivers/usb/host/xhci-rzv2m.c | 38 ++++++++++++++++++++++++++++++++++++++ drivers/usb/host/xhci-rzv2m.h | 16 ++++++++++++++++ 5 files changed, 78 insertions(+) create mode 100644 drivers/usb/host/xhci-rzv2m.c create mode 100644 drivers/usb/host/xhci-rzv2m.h (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 8d799d23c476..662a8bd9a3af 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -54,6 +54,7 @@ config USB_XHCI_PCI_RENESAS config USB_XHCI_PLATFORM tristate "Generic xHCI driver for a platform device" select USB_XHCI_RCAR if ARCH_RENESAS + select USB_XHCI_RZV2M if ARCH_R9A09G011 help Adds an xHCI host driver for a generic platform device, which provides a memory space and an irq. @@ -95,6 +96,15 @@ config USB_XHCI_RCAR Say 'Y' to enable the support for the xHCI host controller found in Renesas R-Car ARM SoCs. +config USB_XHCI_RZV2M + tristate "xHCI support for Renesas RZ/V2M SoC" + depends on USB_XHCI_PLATFORM + depends on ARCH_R9A09G011 || COMPILE_TEST + select USB_RZV2M_USB3DRD + help + Say 'Y' to enable the support for the xHCI host controller + found in Renesas RZ/V2M SoC. + config USB_XHCI_TEGRA tristate "xHCI support for NVIDIA Tegra SoCs" depends on PHY_TEGRA_XUSB diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 6d8ee264c9b2..6b1f9317f116 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -28,6 +28,9 @@ endif ifneq ($(CONFIG_USB_XHCI_RCAR), ) xhci-plat-hcd-y += xhci-rcar.o endif +ifneq ($(CONFIG_USB_XHCI_RZV2M), ) + xhci-plat-hcd-y += xhci-rzv2m.o +endif ifneq ($(CONFIG_DEBUG_FS),) xhci-hcd-y += xhci-debugfs.o diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index c5fc175a5fd1..57269f1f318e 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -25,6 +25,7 @@ #include "xhci-plat.h" #include "xhci-mvebu.h" #include "xhci-rcar.h" +#include "xhci-rzv2m.h" static struct hc_driver __read_mostly xhci_plat_hc_driver; @@ -123,6 +124,13 @@ static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen3 = { SET_XHCI_PLAT_PRIV_FOR_RCAR(XHCI_RCAR_FIRMWARE_NAME_V3) }; +static const struct xhci_plat_priv xhci_plat_renesas_rzv2m = { + .quirks = XHCI_NO_64BIT_SUPPORT | XHCI_TRUST_TX_LENGTH | + XHCI_SLOW_SUSPEND, + .init_quirk = xhci_rzv2m_init_quirk, + .plat_start = xhci_rzv2m_start, +}; + static const struct xhci_plat_priv xhci_plat_brcm = { .quirks = XHCI_RESET_ON_RESUME | XHCI_SUSPEND_RESUME_CLKS, }; @@ -162,6 +170,9 @@ static const struct of_device_id usb_xhci_of_match[] = { }, { .compatible = "renesas,rcar-gen3-xhci", .data = &xhci_plat_renesas_rcar_gen3, + }, { + .compatible = "renesas,rzv2m-xhci", + .data = &xhci_plat_renesas_rzv2m, }, { .compatible = "brcm,xhci-brcm-v2", .data = &xhci_plat_brcm, diff --git a/drivers/usb/host/xhci-rzv2m.c b/drivers/usb/host/xhci-rzv2m.c new file mode 100644 index 000000000000..ec65b24eafa8 --- /dev/null +++ b/drivers/usb/host/xhci-rzv2m.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * xHCI host controller driver for RZ/V2M + * + * Copyright (C) 2022 Renesas Electronics Corporation + */ + +#include +#include "xhci-plat.h" +#include "xhci-rzv2m.h" + +#define RZV2M_USB3_INTEN 0x1044 /* Interrupt Enable */ + +#define RZV2M_USB3_INT_XHC_ENA BIT(0) +#define RZV2M_USB3_INT_HSE_ENA BIT(2) +#define RZV2M_USB3_INT_ENA_VAL (RZV2M_USB3_INT_XHC_ENA \ + | RZV2M_USB3_INT_HSE_ENA) + +int xhci_rzv2m_init_quirk(struct usb_hcd *hcd) +{ + struct device *dev = hcd->self.controller; + + rzv2m_usb3drd_reset(dev->parent, true); + + return 0; +} + +void xhci_rzv2m_start(struct usb_hcd *hcd) +{ + u32 int_en; + + if (hcd->regs) { + /* Interrupt Enable */ + int_en = readl(hcd->regs + RZV2M_USB3_INTEN); + int_en |= RZV2M_USB3_INT_ENA_VAL; + writel(int_en, hcd->regs + RZV2M_USB3_INTEN); + } +} diff --git a/drivers/usb/host/xhci-rzv2m.h b/drivers/usb/host/xhci-rzv2m.h new file mode 100644 index 000000000000..12448b0e8d5b --- /dev/null +++ b/drivers/usb/host/xhci-rzv2m.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __XHCI_RZV2M_H +#define __XHCI_RZV2M_H + +#if IS_ENABLED(CONFIG_USB_XHCI_RZV2M) +void xhci_rzv2m_start(struct usb_hcd *hcd); +int xhci_rzv2m_init_quirk(struct usb_hcd *hcd); +#else +static inline void xhci_rzv2m_start(struct usb_hcd *hcd) {} +static inline int xhci_rzv2m_init_quirk(struct usb_hcd *hcd) +{ + return -EINVAL; +} +#endif + +#endif /* __XHCI_RZV2M_H */ -- cgit v1.2.3 From ff826648e1059606f8418f12b69a4b15a1eed1ba Mon Sep 17 00:00:00 2001 From: Mark Tomlinson Date: Fri, 27 Jan 2023 15:47:34 +1300 Subject: USB: MAX3421: Handle USB NAK correctly A USB peripheral can respond with a NAK if it is not yet ready to send/receive data. In this case, the transaction should be retried. The MAX3421 driver did do this, and switched to a different type of retry after a number of 'fast' retries. On at least some USB flash devices, this second type of retry never succeeds. This patch changes the behaviour so that 'fast' retries continue. Signed-off-by: Mark Tomlinson Link: https://lore.kernel.org/r/20230127024734.8777-1-mark.tomlinson@alliedtelesis.co.nz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/max3421-hcd.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c index 352e3ac2b377..9a87056fc738 100644 --- a/drivers/usb/host/max3421-hcd.c +++ b/drivers/usb/host/max3421-hcd.c @@ -72,12 +72,6 @@ #define USB_MAX_FRAME_NUMBER 0x7ff #define USB_MAX_RETRIES 3 /* # of retries before error is reported */ -/* - * Max. # of times we're willing to retransmit a request immediately in - * resposne to a NAK. Afterwards, we fall back on trying once a frame. - */ -#define NAK_MAX_FAST_RETRANSMITS 2 - #define POWER_BUDGET 500 /* in mA; use 8 for low-power port testing */ /* Port-change mask: */ @@ -924,11 +918,8 @@ max3421_handle_error(struct usb_hcd *hcd, u8 hrsl) * Device wasn't ready for data or has no data * available: retry the packet again. */ - if (max3421_ep->naks++ < NAK_MAX_FAST_RETRANSMITS) { - max3421_next_transfer(hcd, 1); - switch_sndfifo = 0; - } else - max3421_slow_retransmit(hcd); + max3421_next_transfer(hcd, 1); + switch_sndfifo = 0; break; } if (switch_sndfifo) -- cgit v1.2.3 From ec5499d338ece9db9b7590649d3cfcc4d7f9603d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 31 Jan 2023 16:04:31 +0100 Subject: xhci: split out rcar/rz support from xhci-plat.c The USB_XHCI_RZV2M and USB_RENESAS_USB3 select other drivers based on the enabled SoC types, which leads to build failures when the dependencies are not met: WARNING: unmet direct dependencies detected for USB_RZV2M_USB3DRD Depends on [n]: USB_SUPPORT [=y] && USB_GADGET [=n] && (ARCH_R9A09G011 [=n] || COMPILE_TEST [=y]) Selected by [m]: - USB_XHCI_RZV2M [=m] && USB_SUPPORT [=y] && USB [=y] && USB_XHCI_HCD [=m] && USB_XHCI_PLATFORM [=m] && (ARCH_R9A09G011 [=n] || COMPILE_TEST [=y]) ERROR: modpost: "rzv2m_usb3drd_reset" [drivers/usb/host/xhci-plat-hcd.ko] undefined! The xhci-rcar driver has a reverse dependency with the xhci core, and it depends on the UDC driver in turn. To untangle this, make the xhci-rcar.ko driver a standalone module that just calls into the xhci-plat.ko module like other drivers do. This allows handling the dependency on the USB_RZV2M_USB3DRD driver to only affect the xhci-rcar module and simplify the xhci-plat module. It also allows leaving out the hacks for broken dma mask and nested devices from the rcar side and keep that only in the generic xhci driver. As a future cleanup, the marvell and dwc3 specific bits of xhci-plat.c could be moved out as well, but that is not required for this bugfix. Fixes: c52c9acc415e ("xhci: host: Add Renesas RZ/V2M SoC support") Signed-off-by: Arnd Bergmann Tested-by: Biju Das Link: https://lore.kernel.org/r/20230131150531.12347-1-arnd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 9 ++- drivers/usb/host/Makefile | 11 ++-- drivers/usb/host/xhci-plat.c | 127 +++++++++++++++++-------------------------- drivers/usb/host/xhci-plat.h | 7 +++ drivers/usb/host/xhci-rcar.c | 102 ++++++++++++++++++++++++++++++++-- drivers/usb/host/xhci-rcar.h | 55 ------------------- 6 files changed, 163 insertions(+), 148 deletions(-) delete mode 100644 drivers/usb/host/xhci-rcar.h (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 662a8bd9a3af..0a54190bb097 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -53,8 +53,6 @@ config USB_XHCI_PCI_RENESAS config USB_XHCI_PLATFORM tristate "Generic xHCI driver for a platform device" - select USB_XHCI_RCAR if ARCH_RENESAS - select USB_XHCI_RZV2M if ARCH_R9A09G011 help Adds an xHCI host driver for a generic platform device, which provides a memory space and an irq. @@ -92,15 +90,16 @@ config USB_XHCI_RCAR tristate "xHCI support for Renesas R-Car SoCs" depends on USB_XHCI_PLATFORM depends on ARCH_RENESAS || COMPILE_TEST + default ARCH_RENESAS help Say 'Y' to enable the support for the xHCI host controller found in Renesas R-Car ARM SoCs. config USB_XHCI_RZV2M - tristate "xHCI support for Renesas RZ/V2M SoC" - depends on USB_XHCI_PLATFORM + bool "xHCI support for Renesas RZ/V2M SoC" + depends on USB_XHCI_RCAR depends on ARCH_R9A09G011 || COMPILE_TEST - select USB_RZV2M_USB3DRD + depends on USB_RZV2M_USB3DRD=y || (USB_RZV2M_USB3DRD=USB_XHCI_RCAR) help Say 'Y' to enable the support for the xHCI host controller found in Renesas RZ/V2M SoC. diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 6b1f9317f116..5a13712f367d 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -25,17 +25,13 @@ xhci-plat-hcd-y := xhci-plat.o ifneq ($(CONFIG_USB_XHCI_MVEBU), ) xhci-plat-hcd-y += xhci-mvebu.o endif -ifneq ($(CONFIG_USB_XHCI_RCAR), ) - xhci-plat-hcd-y += xhci-rcar.o -endif -ifneq ($(CONFIG_USB_XHCI_RZV2M), ) - xhci-plat-hcd-y += xhci-rzv2m.o -endif - ifneq ($(CONFIG_DEBUG_FS),) xhci-hcd-y += xhci-debugfs.o endif +xhci-rcar-hcd-y += xhci-rcar.o +xhci-rcar-hcd-$(CONFIG_USB_XHCI_RZV2M) += xhci-rzv2m.o + obj-$(CONFIG_USB_PCI) += pci-quirks.o obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o @@ -75,6 +71,7 @@ obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o obj-$(CONFIG_USB_XHCI_PCI_RENESAS) += xhci-pci-renesas.o obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o obj-$(CONFIG_USB_XHCI_HISTB) += xhci-histb.o +obj-$(CONFIG_USB_XHCI_RCAR) += xhci-rcar-hcd.o obj-$(CONFIG_USB_XHCI_MTK) += xhci-mtk-hcd.o obj-$(CONFIG_USB_XHCI_TEGRA) += xhci-tegra.o obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 57269f1f318e..cd17ccab6e00 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -24,8 +24,6 @@ #include "xhci.h" #include "xhci-plat.h" #include "xhci-mvebu.h" -#include "xhci-rcar.h" -#include "xhci-rzv2m.h" static struct hc_driver __read_mostly xhci_plat_hc_driver; @@ -116,21 +114,6 @@ static const struct xhci_plat_priv xhci_plat_marvell_armada3700 = { .init_quirk = xhci_mvebu_a3700_init_quirk, }; -static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen2 = { - SET_XHCI_PLAT_PRIV_FOR_RCAR(XHCI_RCAR_FIRMWARE_NAME_V1) -}; - -static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen3 = { - SET_XHCI_PLAT_PRIV_FOR_RCAR(XHCI_RCAR_FIRMWARE_NAME_V3) -}; - -static const struct xhci_plat_priv xhci_plat_renesas_rzv2m = { - .quirks = XHCI_NO_64BIT_SUPPORT | XHCI_TRUST_TX_LENGTH | - XHCI_SLOW_SUSPEND, - .init_quirk = xhci_rzv2m_init_quirk, - .plat_start = xhci_rzv2m_start, -}; - static const struct xhci_plat_priv xhci_plat_brcm = { .quirks = XHCI_RESET_ON_RESUME | XHCI_SUSPEND_RESUME_CLKS, }; @@ -149,30 +132,6 @@ static const struct of_device_id usb_xhci_of_match[] = { }, { .compatible = "marvell,armada3700-xhci", .data = &xhci_plat_marvell_armada3700, - }, { - .compatible = "renesas,xhci-r8a7790", - .data = &xhci_plat_renesas_rcar_gen2, - }, { - .compatible = "renesas,xhci-r8a7791", - .data = &xhci_plat_renesas_rcar_gen2, - }, { - .compatible = "renesas,xhci-r8a7793", - .data = &xhci_plat_renesas_rcar_gen2, - }, { - .compatible = "renesas,xhci-r8a7795", - .data = &xhci_plat_renesas_rcar_gen3, - }, { - .compatible = "renesas,xhci-r8a7796", - .data = &xhci_plat_renesas_rcar_gen3, - }, { - .compatible = "renesas,rcar-gen2-xhci", - .data = &xhci_plat_renesas_rcar_gen2, - }, { - .compatible = "renesas,rcar-gen3-xhci", - .data = &xhci_plat_renesas_rcar_gen3, - }, { - .compatible = "renesas,rzv2m-xhci", - .data = &xhci_plat_renesas_rzv2m, }, { .compatible = "brcm,xhci-brcm-v2", .data = &xhci_plat_brcm, @@ -185,11 +144,10 @@ static const struct of_device_id usb_xhci_of_match[] = { MODULE_DEVICE_TABLE(of, usb_xhci_of_match); #endif -static int xhci_plat_probe(struct platform_device *pdev) +int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const struct xhci_plat_priv *priv_match) { - const struct xhci_plat_priv *priv_match; const struct hc_driver *driver; - struct device *sysdev, *tmpdev; + struct device *tmpdev; struct xhci_hcd *xhci; struct resource *res; struct usb_hcd *hcd, *usb3_hcd; @@ -207,31 +165,10 @@ static int xhci_plat_probe(struct platform_device *pdev) if (irq < 0) return irq; - /* - * sysdev must point to a device that is known to the system firmware - * or PCI hardware. We handle these three cases here: - * 1. xhci_plat comes from firmware - * 2. xhci_plat is child of a device from firmware (dwc3-plat) - * 3. xhci_plat is grandchild of a pci device (dwc3-pci) - */ - for (sysdev = &pdev->dev; sysdev; sysdev = sysdev->parent) { - if (is_of_node(sysdev->fwnode) || - is_acpi_device_node(sysdev->fwnode)) - break; -#ifdef CONFIG_PCI - else if (sysdev->bus == &pci_bus_type) - break; -#endif - } - if (!sysdev) sysdev = &pdev->dev; - if (WARN_ON(!sysdev->dma_mask)) - /* Platform did not initialize dma_mask */ - ret = dma_coerce_mask_and_coherent(sysdev, DMA_BIT_MASK(64)); - else - ret = dma_set_mask_and_coherent(sysdev, DMA_BIT_MASK(64)); + ret = dma_set_mask_and_coherent(sysdev, DMA_BIT_MASK(64)); if (ret) return ret; @@ -293,11 +230,6 @@ static int xhci_plat_probe(struct platform_device *pdev) if (ret) goto disable_reg_clk; - if (pdev->dev.of_node) - priv_match = of_device_get_match_data(&pdev->dev); - else - priv_match = dev_get_platdata(&pdev->dev); - if (priv_match) { priv = hcd_to_xhci_priv(hcd); /* Just copy data for now */ @@ -411,8 +343,47 @@ disable_runtime: return ret; } +EXPORT_SYMBOL_GPL(xhci_plat_probe); + +static int xhci_generic_plat_probe(struct platform_device *pdev) +{ + const struct xhci_plat_priv *priv_match; + struct device *sysdev; + int ret; + + /* + * sysdev must point to a device that is known to the system firmware + * or PCI hardware. We handle these three cases here: + * 1. xhci_plat comes from firmware + * 2. xhci_plat is child of a device from firmware (dwc3-plat) + * 3. xhci_plat is grandchild of a pci device (dwc3-pci) + */ + for (sysdev = &pdev->dev; sysdev; sysdev = sysdev->parent) { + if (is_of_node(sysdev->fwnode) || + is_acpi_device_node(sysdev->fwnode)) + break; +#ifdef CONFIG_PCI + else if (sysdev->bus == &pci_bus_type) + break; +#endif + } + + if (WARN_ON(!sysdev->dma_mask)) { + /* Platform did not initialize dma_mask */ + ret = dma_coerce_mask_and_coherent(sysdev, DMA_BIT_MASK(64)); + if (ret) + return ret; + } + + if (pdev->dev.of_node) + priv_match = of_device_get_match_data(&pdev->dev); + else + priv_match = dev_get_platdata(&pdev->dev); + + return xhci_plat_probe(pdev, sysdev, priv_match); +} -static int xhci_plat_remove(struct platform_device *dev) +int xhci_plat_remove(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); struct xhci_hcd *xhci = hcd_to_xhci(hcd); @@ -446,6 +417,7 @@ static int xhci_plat_remove(struct platform_device *dev) return 0; } +EXPORT_SYMBOL_GPL(xhci_plat_remove); static int __maybe_unused xhci_plat_suspend(struct device *dev) { @@ -522,13 +494,14 @@ static int __maybe_unused xhci_plat_runtime_resume(struct device *dev) return xhci_resume(xhci, 0); } -static const struct dev_pm_ops xhci_plat_pm_ops = { +const struct dev_pm_ops xhci_plat_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(xhci_plat_suspend, xhci_plat_resume) SET_RUNTIME_PM_OPS(xhci_plat_runtime_suspend, xhci_plat_runtime_resume, NULL) }; +EXPORT_SYMBOL_GPL(xhci_plat_pm_ops); #ifdef CONFIG_ACPI static const struct acpi_device_id usb_xhci_acpi_match[] = { @@ -539,8 +512,8 @@ static const struct acpi_device_id usb_xhci_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, usb_xhci_acpi_match); #endif -static struct platform_driver usb_xhci_driver = { - .probe = xhci_plat_probe, +static struct platform_driver usb_generic_xhci_driver = { + .probe = xhci_generic_plat_probe, .remove = xhci_plat_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { @@ -555,13 +528,13 @@ MODULE_ALIAS("platform:xhci-hcd"); static int __init xhci_plat_init(void) { xhci_init_driver(&xhci_plat_hc_driver, &xhci_plat_overrides); - return platform_driver_register(&usb_xhci_driver); + return platform_driver_register(&usb_generic_xhci_driver); } module_init(xhci_plat_init); static void __exit xhci_plat_exit(void) { - platform_driver_unregister(&usb_xhci_driver); + platform_driver_unregister(&usb_generic_xhci_driver); } module_exit(xhci_plat_exit); diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h index 1fb149d1fbce..83b5b5aa9f8e 100644 --- a/drivers/usb/host/xhci-plat.h +++ b/drivers/usb/host/xhci-plat.h @@ -21,4 +21,11 @@ struct xhci_plat_priv { #define hcd_to_xhci_priv(h) ((struct xhci_plat_priv *)hcd_to_xhci(h)->priv) #define xhci_to_priv(x) ((struct xhci_plat_priv *)(x)->priv) + +int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, + const struct xhci_plat_priv *priv_match); + +int xhci_plat_remove(struct platform_device *dev); +extern const struct dev_pm_ops xhci_plat_pm_ops; + #endif /* _XHCI_PLAT_H */ diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c index aef0258a7160..7f18509a1d39 100644 --- a/drivers/usb/host/xhci-rcar.c +++ b/drivers/usb/host/xhci-rcar.c @@ -10,12 +10,17 @@ #include #include #include +#include #include #include #include "xhci.h" #include "xhci-plat.h" -#include "xhci-rcar.h" +#include "xhci-rzv2m.h" + +#define XHCI_RCAR_FIRMWARE_NAME_V1 "r8a779x_usb3_v1.dlmem" +#define XHCI_RCAR_FIRMWARE_NAME_V2 "r8a779x_usb3_v2.dlmem" +#define XHCI_RCAR_FIRMWARE_NAME_V3 "r8a779x_usb3_v3.dlmem" /* * - The V3 firmware is for almost all R-Car Gen3 (except r8a7795 ES1.x) @@ -108,7 +113,7 @@ static int xhci_rcar_is_gen2(struct device *dev) of_device_is_compatible(node, "renesas,rcar-gen2-xhci"); } -void xhci_rcar_start(struct usb_hcd *hcd) +static void xhci_rcar_start(struct usb_hcd *hcd) { u32 temp; @@ -203,7 +208,7 @@ static bool xhci_rcar_wait_for_pll_active(struct usb_hcd *hcd) } /* This function needs to initialize a "phy" of usb before */ -int xhci_rcar_init_quirk(struct usb_hcd *hcd) +static int xhci_rcar_init_quirk(struct usb_hcd *hcd) { /* If hcd->regs is NULL, we don't just call the following function */ if (!hcd->regs) @@ -215,7 +220,7 @@ int xhci_rcar_init_quirk(struct usb_hcd *hcd) return xhci_rcar_download_firmware(hcd); } -int xhci_rcar_resume_quirk(struct usb_hcd *hcd) +static int xhci_rcar_resume_quirk(struct usb_hcd *hcd) { int ret; @@ -225,3 +230,92 @@ int xhci_rcar_resume_quirk(struct usb_hcd *hcd) return ret; } + +/* + * On R-Car Gen2 and Gen3, the AC64 bit (bit 0) of HCCPARAMS1 is set + * to 1. However, these SoCs don't support 64-bit address memory + * pointers. So, this driver clears the AC64 bit of xhci->hcc_params + * to call dma_set_coherent_mask(dev, DMA_BIT_MASK(32)) in + * xhci_gen_setup() by using the XHCI_NO_64BIT_SUPPORT quirk. + * + * And, since the firmware/internal CPU control the USBSTS.STS_HALT + * and the process speed is down when the roothub port enters U3, + * long delay for the handshake of STS_HALT is neeed in xhci_suspend() + * by using the XHCI_SLOW_SUSPEND quirk. + */ +#define SET_XHCI_PLAT_PRIV_FOR_RCAR(firmware) \ + .firmware_name = firmware, \ + .quirks = XHCI_NO_64BIT_SUPPORT | XHCI_TRUST_TX_LENGTH | \ + XHCI_SLOW_SUSPEND, \ + .init_quirk = xhci_rcar_init_quirk, \ + .plat_start = xhci_rcar_start, \ + .resume_quirk = xhci_rcar_resume_quirk, + +static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen2 = { + SET_XHCI_PLAT_PRIV_FOR_RCAR(XHCI_RCAR_FIRMWARE_NAME_V1) +}; + +static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen3 = { + SET_XHCI_PLAT_PRIV_FOR_RCAR(XHCI_RCAR_FIRMWARE_NAME_V3) +}; + +static const struct xhci_plat_priv xhci_plat_renesas_rzv2m = { + .quirks = XHCI_NO_64BIT_SUPPORT | XHCI_TRUST_TX_LENGTH | + XHCI_SLOW_SUSPEND, + .init_quirk = xhci_rzv2m_init_quirk, + .plat_start = xhci_rzv2m_start, +}; + +static const struct of_device_id usb_xhci_of_match[] = { + { + .compatible = "renesas,xhci-r8a7790", + .data = &xhci_plat_renesas_rcar_gen2, + }, { + .compatible = "renesas,xhci-r8a7791", + .data = &xhci_plat_renesas_rcar_gen2, + }, { + .compatible = "renesas,xhci-r8a7793", + .data = &xhci_plat_renesas_rcar_gen2, + }, { + .compatible = "renesas,xhci-r8a7795", + .data = &xhci_plat_renesas_rcar_gen3, + }, { + .compatible = "renesas,xhci-r8a7796", + .data = &xhci_plat_renesas_rcar_gen3, + }, { + .compatible = "renesas,rcar-gen2-xhci", + .data = &xhci_plat_renesas_rcar_gen2, + }, { + .compatible = "renesas,rcar-gen3-xhci", + .data = &xhci_plat_renesas_rcar_gen3, + }, { + .compatible = "renesas,rzv2m-xhci", + .data = &xhci_plat_renesas_rzv2m, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, usb_xhci_of_match); + +static int xhci_renesas_probe(struct platform_device *pdev) +{ + const struct xhci_plat_priv *priv_match; + + priv_match = of_device_get_match_data(&pdev->dev); + + return xhci_plat_probe(pdev, NULL, priv_match); +} + +static struct platform_driver usb_xhci_renesas_driver = { + .probe = xhci_renesas_probe, + .remove = xhci_plat_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "xhci-renesas-hcd", + .pm = &xhci_plat_pm_ops, + .of_match_table = of_match_ptr(usb_xhci_of_match), + }, +}; +module_platform_driver(usb_xhci_renesas_driver); + +MODULE_DESCRIPTION("xHCI Platform Host Controller Driver for Renesas R-Car and RZ"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/xhci-rcar.h b/drivers/usb/host/xhci-rcar.h deleted file mode 100644 index 048ad3b8a6c7..000000000000 --- a/drivers/usb/host/xhci-rcar.h +++ /dev/null @@ -1,55 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * drivers/usb/host/xhci-rcar.h - * - * Copyright (C) 2014 Renesas Electronics Corporation - */ - -#ifndef _XHCI_RCAR_H -#define _XHCI_RCAR_H - -#define XHCI_RCAR_FIRMWARE_NAME_V1 "r8a779x_usb3_v1.dlmem" -#define XHCI_RCAR_FIRMWARE_NAME_V2 "r8a779x_usb3_v2.dlmem" -#define XHCI_RCAR_FIRMWARE_NAME_V3 "r8a779x_usb3_v3.dlmem" - -#if IS_ENABLED(CONFIG_USB_XHCI_RCAR) -void xhci_rcar_start(struct usb_hcd *hcd); -int xhci_rcar_init_quirk(struct usb_hcd *hcd); -int xhci_rcar_resume_quirk(struct usb_hcd *hcd); -#else -static inline void xhci_rcar_start(struct usb_hcd *hcd) -{ -} - -static inline int xhci_rcar_init_quirk(struct usb_hcd *hcd) -{ - return 0; -} - -static inline int xhci_rcar_resume_quirk(struct usb_hcd *hcd) -{ - return 0; -} -#endif - -/* - * On R-Car Gen2 and Gen3, the AC64 bit (bit 0) of HCCPARAMS1 is set - * to 1. However, these SoCs don't support 64-bit address memory - * pointers. So, this driver clears the AC64 bit of xhci->hcc_params - * to call dma_set_coherent_mask(dev, DMA_BIT_MASK(32)) in - * xhci_gen_setup() by using the XHCI_NO_64BIT_SUPPORT quirk. - * - * And, since the firmware/internal CPU control the USBSTS.STS_HALT - * and the process speed is down when the roothub port enters U3, - * long delay for the handshake of STS_HALT is neeed in xhci_suspend() - * by using the XHCI_SLOW_SUSPEND quirk. - */ -#define SET_XHCI_PLAT_PRIV_FOR_RCAR(firmware) \ - .firmware_name = firmware, \ - .quirks = XHCI_NO_64BIT_SUPPORT | XHCI_TRUST_TX_LENGTH | \ - XHCI_SLOW_SUSPEND, \ - .init_quirk = xhci_rcar_init_quirk, \ - .plat_start = xhci_rcar_start, \ - .resume_quirk = xhci_rcar_resume_quirk, - -#endif /* _XHCI_RCAR_H */ -- cgit v1.2.3 From 8c1cbec9db1ab044167a7594c88bb5906c9d3ee4 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Thu, 2 Feb 2023 17:04:55 +0200 Subject: xhci: fix event ring segment table related masks and variables in header xHC controller can supports up to 1024 interrupters. To fit these change the max_interrupters varable from u8 to u16. Add a separate mask for the reserve and preserve bits [5:0] in the erst base register and use it instead of the ERST_PRT_MASK. ERSR_PTR_MASK [3:0] is intended for masking bits in the event ring dequeue pointer register. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-2-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 4 ++-- drivers/usb/host/xhci.h | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 81ca2bc1f0be..679befa97c7a 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -2529,8 +2529,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) "// Set ERST base address for ir_set 0 = 0x%llx", (unsigned long long)xhci->erst.erst_dma_addr); val_64 = xhci_read_64(xhci, &xhci->ir_set->erst_base); - val_64 &= ERST_PTR_MASK; - val_64 |= (xhci->erst.erst_dma_addr & (u64) ~ERST_PTR_MASK); + val_64 &= ERST_BASE_RSVDP; + val_64 |= (xhci->erst.erst_dma_addr & (u64) ~ERST_BASE_RSVDP); xhci_write_64(xhci, val_64, &xhci->ir_set->erst_base); /* Set the event ring dequeue address */ diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 7547037b57bf..0dd92f9089fe 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -513,6 +513,9 @@ struct xhci_intr_reg { /* Preserve bits 16:31 of erst_size */ #define ERST_SIZE_MASK (0xffff << 16) +/* erst_base bitmasks */ +#define ERST_BASE_RSVDP (0x3f) + /* erst_dequeue bitmasks */ /* Dequeue ERST Segment Index (DESI) - Segment number (or alias) * where the current dequeue pointer lies. This is an optional HW hint. @@ -1774,7 +1777,7 @@ struct xhci_hcd { u8 sbrn; u16 hci_version; u8 max_slots; - u8 max_interrupters; + u16 max_interrupters; u8 max_ports; u8 isoc_threshold; /* imod_interval in ns (I * 250ns) */ -- cgit v1.2.3 From 54f9927dfe2266402a226d5f51d38236bdca0590 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Thu, 2 Feb 2023 17:04:56 +0200 Subject: xhci: remove xhci_test_trb_in_td_math early development check Time to remove this test trb in td math check that was added in early stage of xhci driver development. It verified that the size, alignment and boundaries of the event and command rings allocated by the driver itself are correct. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-3-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 160 -------------------------------------------- 1 file changed, 160 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 679befa97c7a..bf9bb29f924b 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1929,164 +1929,6 @@ no_bw: xhci->usb3_rhub.bus_state.bus_suspended = 0; } -static int xhci_test_trb_in_td(struct xhci_hcd *xhci, - struct xhci_segment *input_seg, - union xhci_trb *start_trb, - union xhci_trb *end_trb, - dma_addr_t input_dma, - struct xhci_segment *result_seg, - char *test_name, int test_number) -{ - unsigned long long start_dma; - unsigned long long end_dma; - struct xhci_segment *seg; - - start_dma = xhci_trb_virt_to_dma(input_seg, start_trb); - end_dma = xhci_trb_virt_to_dma(input_seg, end_trb); - - seg = trb_in_td(xhci, input_seg, start_trb, end_trb, input_dma, false); - if (seg != result_seg) { - xhci_warn(xhci, "WARN: %s TRB math test %d failed!\n", - test_name, test_number); - xhci_warn(xhci, "Tested TRB math w/ seg %p and " - "input DMA 0x%llx\n", - input_seg, - (unsigned long long) input_dma); - xhci_warn(xhci, "starting TRB %p (0x%llx DMA), " - "ending TRB %p (0x%llx DMA)\n", - start_trb, start_dma, - end_trb, end_dma); - xhci_warn(xhci, "Expected seg %p, got seg %p\n", - result_seg, seg); - trb_in_td(xhci, input_seg, start_trb, end_trb, input_dma, - true); - return -1; - } - return 0; -} - -/* TRB math checks for xhci_trb_in_td(), using the command and event rings. */ -static int xhci_check_trb_in_td_math(struct xhci_hcd *xhci) -{ - struct { - dma_addr_t input_dma; - struct xhci_segment *result_seg; - } simple_test_vector [] = { - /* A zeroed DMA field should fail */ - { 0, NULL }, - /* One TRB before the ring start should fail */ - { xhci->event_ring->first_seg->dma - 16, NULL }, - /* One byte before the ring start should fail */ - { xhci->event_ring->first_seg->dma - 1, NULL }, - /* Starting TRB should succeed */ - { xhci->event_ring->first_seg->dma, xhci->event_ring->first_seg }, - /* Ending TRB should succeed */ - { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 1)*16, - xhci->event_ring->first_seg }, - /* One byte after the ring end should fail */ - { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 1)*16 + 1, NULL }, - /* One TRB after the ring end should fail */ - { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT)*16, NULL }, - /* An address of all ones should fail */ - { (dma_addr_t) (~0), NULL }, - }; - struct { - struct xhci_segment *input_seg; - union xhci_trb *start_trb; - union xhci_trb *end_trb; - dma_addr_t input_dma; - struct xhci_segment *result_seg; - } complex_test_vector [] = { - /* Test feeding a valid DMA address from a different ring */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = xhci->event_ring->first_seg->trbs, - .end_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1], - .input_dma = xhci->cmd_ring->first_seg->dma, - .result_seg = NULL, - }, - /* Test feeding a valid end TRB from a different ring */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = xhci->event_ring->first_seg->trbs, - .end_trb = &xhci->cmd_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1], - .input_dma = xhci->cmd_ring->first_seg->dma, - .result_seg = NULL, - }, - /* Test feeding a valid start and end TRB from a different ring */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = xhci->cmd_ring->first_seg->trbs, - .end_trb = &xhci->cmd_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1], - .input_dma = xhci->cmd_ring->first_seg->dma, - .result_seg = NULL, - }, - /* TRB in this ring, but after this TD */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = &xhci->event_ring->first_seg->trbs[0], - .end_trb = &xhci->event_ring->first_seg->trbs[3], - .input_dma = xhci->event_ring->first_seg->dma + 4*16, - .result_seg = NULL, - }, - /* TRB in this ring, but before this TD */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = &xhci->event_ring->first_seg->trbs[3], - .end_trb = &xhci->event_ring->first_seg->trbs[6], - .input_dma = xhci->event_ring->first_seg->dma + 2*16, - .result_seg = NULL, - }, - /* TRB in this ring, but after this wrapped TD */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3], - .end_trb = &xhci->event_ring->first_seg->trbs[1], - .input_dma = xhci->event_ring->first_seg->dma + 2*16, - .result_seg = NULL, - }, - /* TRB in this ring, but before this wrapped TD */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3], - .end_trb = &xhci->event_ring->first_seg->trbs[1], - .input_dma = xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 4)*16, - .result_seg = NULL, - }, - /* TRB not in this ring, and we have a wrapped TD */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3], - .end_trb = &xhci->event_ring->first_seg->trbs[1], - .input_dma = xhci->cmd_ring->first_seg->dma + 2*16, - .result_seg = NULL, - }, - }; - - unsigned int num_tests; - int i, ret; - - num_tests = ARRAY_SIZE(simple_test_vector); - for (i = 0; i < num_tests; i++) { - ret = xhci_test_trb_in_td(xhci, - xhci->event_ring->first_seg, - xhci->event_ring->first_seg->trbs, - &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1], - simple_test_vector[i].input_dma, - simple_test_vector[i].result_seg, - "Simple", i); - if (ret < 0) - return ret; - } - - num_tests = ARRAY_SIZE(complex_test_vector); - for (i = 0; i < num_tests; i++) { - ret = xhci_test_trb_in_td(xhci, - complex_test_vector[i].input_seg, - complex_test_vector[i].start_trb, - complex_test_vector[i].end_trb, - complex_test_vector[i].input_dma, - complex_test_vector[i].result_seg, - "Complex", i); - if (ret < 0) - return ret; - } - xhci_dbg(xhci, "TRB math tests passed.\n"); - return 0; -} - static void xhci_set_hc_event_deq(struct xhci_hcd *xhci) { u64 temp; @@ -2506,8 +2348,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) 0, flags); if (!xhci->event_ring) goto fail; - if (xhci_check_trb_in_td_math(xhci) < 0) - goto fail; ret = xhci_alloc_erst(xhci, xhci->event_ring, &xhci->erst, flags); if (ret) -- cgit v1.2.3 From b17a57f89f69069458d0a9d9b04281ce48da7ebb Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Thu, 2 Feb 2023 17:04:57 +0200 Subject: xhci: Refactor interrupter code for initial multi interrupter support. xHC supports several interrupters, each with its own mmio register set, event ring and MSI/MSI-X vector. Transfers can be assigned different interrupters when queued. See xhci 4.17 for details. Current driver only supports one interrupter. Create a xhci_interrupter structure containing an event ring, pointer to mmio registers for this interrupter, variables to store registers over s3 suspend, erst, etc. Add functions to create and free an interrupter, and pass an interrupter pointer to functions that deal with events. Secondary interrupters are also useful without having an interrupt vector. One use case is the xHCI audio sideband offloading where a DSP can take care of specific audio endpoints. When all transfer events of an offloaded endpoint can be mapped to a separate interrupter event ring the DSP can poll this ring, and we can mask these events preventing waking up the CPU. Only minor functional changes such as clearing some of the interrupter registers when freeing the interrupter. Still create only one primary interrupter. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-4-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-debugfs.c | 2 +- drivers/usb/host/xhci-mem.c | 168 ++++++++++++++++++++++++++-------------- drivers/usb/host/xhci-ring.c | 68 ++++++++-------- drivers/usb/host/xhci.c | 54 ++++++++----- drivers/usb/host/xhci.h | 24 +++--- 5 files changed, 196 insertions(+), 120 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c index dc832ddf7033..0bc7fe11f749 100644 --- a/drivers/usb/host/xhci-debugfs.c +++ b/drivers/usb/host/xhci-debugfs.c @@ -692,7 +692,7 @@ void xhci_debugfs_init(struct xhci_hcd *xhci) "command-ring", xhci->debugfs_root); - xhci_debugfs_create_ring_dir(xhci, &xhci->event_ring, + xhci_debugfs_create_ring_dir(xhci, &xhci->interrupter->event_ring, "event-ring", xhci->debugfs_root); diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index bf9bb29f924b..6b83c5c35cf8 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1819,17 +1819,43 @@ int xhci_alloc_erst(struct xhci_hcd *xhci, return 0; } -void xhci_free_erst(struct xhci_hcd *xhci, struct xhci_erst *erst) +static void +xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir) { - size_t size; struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + size_t erst_size; + u64 tmp64; + u32 tmp; - size = sizeof(struct xhci_erst_entry) * (erst->num_entries); - if (erst->entries) - dma_free_coherent(dev, size, - erst->entries, - erst->erst_dma_addr); - erst->entries = NULL; + if (!ir) + return; + + erst_size = sizeof(struct xhci_erst_entry) * (ir->erst.num_entries); + if (ir->erst.entries) + dma_free_coherent(dev, erst_size, + ir->erst.entries, + ir->erst.erst_dma_addr); + ir->erst.entries = NULL; + + /* + * Clean out interrupter registers except ERSTBA. Clearing either the + * low or high 32 bits of ERSTBA immediately causes the controller to + * dereference the partially cleared 64 bit address, causing IOMMU error. + */ + tmp = readl(&ir->ir_set->erst_size); + tmp &= ERST_SIZE_MASK; + writel(tmp, &ir->ir_set->erst_size); + + tmp64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); + tmp64 &= (u64) ERST_PTR_MASK; + xhci_write_64(xhci, tmp64, &ir->ir_set->erst_dequeue); + + /* free interrrupter event ring */ + if (ir->event_ring) + xhci_ring_free(xhci, ir->event_ring); + ir->event_ring = NULL; + + kfree(ir); } void xhci_mem_cleanup(struct xhci_hcd *xhci) @@ -1839,12 +1865,9 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) cancel_delayed_work_sync(&xhci->cmd_timer); - xhci_free_erst(xhci, &xhci->erst); - - if (xhci->event_ring) - xhci_ring_free(xhci, xhci->event_ring); - xhci->event_ring = NULL; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed event ring"); + xhci_free_interrupter(xhci, xhci->interrupter); + xhci->interrupter = NULL; + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed primary event ring"); if (xhci->cmd_ring) xhci_ring_free(xhci, xhci->cmd_ring); @@ -1929,18 +1952,18 @@ no_bw: xhci->usb3_rhub.bus_state.bus_suspended = 0; } -static void xhci_set_hc_event_deq(struct xhci_hcd *xhci) +static void xhci_set_hc_event_deq(struct xhci_hcd *xhci, struct xhci_interrupter *ir) { u64 temp; dma_addr_t deq; - deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, - xhci->event_ring->dequeue); + deq = xhci_trb_virt_to_dma(ir->event_ring->deq_seg, + ir->event_ring->dequeue); if (!deq) xhci_warn(xhci, "WARN something wrong with SW event ring " "dequeue ptr.\n"); /* Update HC event ring dequeue pointer */ - temp = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + temp = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); temp &= ERST_PTR_MASK; /* Don't clear the EHB bit (which is RW1C) because * there might be more events to service. @@ -1950,7 +1973,7 @@ static void xhci_set_hc_event_deq(struct xhci_hcd *xhci) "// Write event ring dequeue pointer, " "preserving EHB bit"); xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | temp, - &xhci->ir_set->erst_dequeue); + &ir->ir_set->erst_dequeue); } static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, @@ -2217,6 +2240,68 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) return 0; } +static struct xhci_interrupter * +xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int intr_num, gfp_t flags) +{ + struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + struct xhci_interrupter *ir; + u64 erst_base; + u32 erst_size; + int ret; + + if (intr_num > xhci->max_interrupters) { + xhci_warn(xhci, "Can't allocate interrupter %d, max interrupters %d\n", + intr_num, xhci->max_interrupters); + return NULL; + } + + if (xhci->interrupter) { + xhci_warn(xhci, "Can't allocate already set up interrupter %d\n", intr_num); + return NULL; + } + + ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev)); + if (!ir) + return NULL; + + ir->ir_set = &xhci->run_regs->ir_set[intr_num]; + ir->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT, + 0, flags); + if (!ir->event_ring) { + xhci_warn(xhci, "Failed to allocate interrupter %d event ring\n", intr_num); + goto fail_ir; + } + + ret = xhci_alloc_erst(xhci, ir->event_ring, &ir->erst, flags); + if (ret) { + xhci_warn(xhci, "Failed to allocate interrupter %d erst\n", intr_num); + goto fail_ev; + + } + /* set ERST count with the number of entries in the segment table */ + erst_size = readl(&ir->ir_set->erst_size); + erst_size &= ERST_SIZE_MASK; + erst_size |= ERST_NUM_SEGS; + writel(erst_size, &ir->ir_set->erst_size); + + erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base); + erst_base &= ERST_PTR_MASK; + erst_base |= (ir->erst.erst_dma_addr & (u64) ~ERST_PTR_MASK); + xhci_write_64(xhci, erst_base, &ir->ir_set->erst_base); + + /* Set the event ring dequeue address of this interrupter */ + xhci_set_hc_event_deq(xhci, ir); + + return ir; + +fail_ev: + xhci_ring_free(xhci, ir->event_ring); +fail_ir: + kfree(ir); + + return NULL; +} + int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) { dma_addr_t dma; @@ -2224,7 +2309,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) unsigned int val, val2; u64 val_64; u32 page_size, temp; - int i, ret; + int i; INIT_LIST_HEAD(&xhci->cmd_list); @@ -2337,46 +2422,13 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) " from cap regs base addr", val); xhci->dba = (void __iomem *) xhci->cap_regs + val; /* Set ir_set to interrupt register set 0 */ - xhci->ir_set = &xhci->run_regs->ir_set[0]; - - /* - * Event ring setup: Allocate a normal ring, but also setup - * the event ring segment table (ERST). Section 4.9.3. - */ - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Allocating event ring"); - xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT, - 0, flags); - if (!xhci->event_ring) - goto fail; - - ret = xhci_alloc_erst(xhci, xhci->event_ring, &xhci->erst, flags); - if (ret) - goto fail; - - /* set ERST count with the number of entries in the segment table */ - val = readl(&xhci->ir_set->erst_size); - val &= ERST_SIZE_MASK; - val |= ERST_NUM_SEGS; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Write ERST size = %i to ir_set 0 (some bits preserved)", - val); - writel(val, &xhci->ir_set->erst_size); + /* allocate and set up primary interrupter with an event ring. */ xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Set ERST entries to point to event ring."); - /* set the segment table base address */ - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Set ERST base address for ir_set 0 = 0x%llx", - (unsigned long long)xhci->erst.erst_dma_addr); - val_64 = xhci_read_64(xhci, &xhci->ir_set->erst_base); - val_64 &= ERST_BASE_RSVDP; - val_64 |= (xhci->erst.erst_dma_addr & (u64) ~ERST_BASE_RSVDP); - xhci_write_64(xhci, val_64, &xhci->ir_set->erst_base); - - /* Set the event ring dequeue address */ - xhci_set_hc_event_deq(xhci); - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "Wrote ERST address to ir_set 0."); + "Allocating primary event ring"); + xhci->interrupter = xhci_alloc_interrupter(xhci, 0, flags); + if (!xhci->interrupter) + goto fail; xhci->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX; diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 5f1ecdee2c1c..451d48b87cf7 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1833,7 +1833,8 @@ static void xhci_cavium_reset_phy_quirk(struct xhci_hcd *xhci) } static void handle_port_status(struct xhci_hcd *xhci, - union xhci_trb *event) + struct xhci_interrupter *ir, + union xhci_trb *event) { struct usb_hcd *hcd; u32 port_id; @@ -1856,7 +1857,7 @@ static void handle_port_status(struct xhci_hcd *xhci, if ((port_id <= 0) || (port_id > max_ports)) { xhci_warn(xhci, "Port change event with invalid port ID %d\n", port_id); - inc_deq(xhci, xhci->event_ring); + inc_deq(xhci, ir->event_ring); return; } @@ -1986,7 +1987,7 @@ static void handle_port_status(struct xhci_hcd *xhci, cleanup: /* Update event ring dequeue pointer before dropping the lock */ - inc_deq(xhci, xhci->event_ring); + inc_deq(xhci, ir->event_ring); /* Don't make the USB core poll the roothub if we got a bad port status * change event. Besides, at that point we can't tell which roothub @@ -2519,7 +2520,8 @@ finish_td: * At this point, the host controller is probably hosed and should be reset. */ static int handle_tx_event(struct xhci_hcd *xhci, - struct xhci_transfer_event *event) + struct xhci_interrupter *ir, + struct xhci_transfer_event *event) { struct xhci_virt_ep *ep; struct xhci_ring *ep_ring; @@ -2868,7 +2870,7 @@ cleanup: * processing missed tds. */ if (!handling_skipped_tds) - inc_deq(xhci, xhci->event_ring); + inc_deq(xhci, ir->event_ring); /* * If ep->skip is set, it means there are missed tds on the @@ -2883,8 +2885,8 @@ cleanup: err_out: xhci_err(xhci, "@%016llx %08x %08x %08x %08x\n", (unsigned long long) xhci_trb_virt_to_dma( - xhci->event_ring->deq_seg, - xhci->event_ring->dequeue), + ir->event_ring->deq_seg, + ir->event_ring->dequeue), lower_32_bits(le64_to_cpu(event->buffer)), upper_32_bits(le64_to_cpu(event->buffer)), le32_to_cpu(event->transfer_len), @@ -2898,7 +2900,7 @@ err_out: * Returns >0 for "possibly more events to process" (caller should call again), * otherwise 0 if done. In future, <0 returns should indicate error code. */ -static int xhci_handle_event(struct xhci_hcd *xhci) +static int xhci_handle_event(struct xhci_hcd *xhci, struct xhci_interrupter *ir) { union xhci_trb *event; int update_ptrs = 1; @@ -2906,18 +2908,18 @@ static int xhci_handle_event(struct xhci_hcd *xhci) int ret; /* Event ring hasn't been allocated yet. */ - if (!xhci->event_ring || !xhci->event_ring->dequeue) { - xhci_err(xhci, "ERROR event ring not ready\n"); + if (!ir || !ir->event_ring || !ir->event_ring->dequeue) { + xhci_err(xhci, "ERROR interrupter not ready\n"); return -ENOMEM; } - event = xhci->event_ring->dequeue; + event = ir->event_ring->dequeue; /* Does the HC or OS own the TRB? */ if ((le32_to_cpu(event->event_cmd.flags) & TRB_CYCLE) != - xhci->event_ring->cycle_state) + ir->event_ring->cycle_state) return 0; - trace_xhci_handle_event(xhci->event_ring, &event->generic); + trace_xhci_handle_event(ir->event_ring, &event->generic); /* * Barrier between reading the TRB_CYCLE (valid) flag above and any @@ -2932,11 +2934,11 @@ static int xhci_handle_event(struct xhci_hcd *xhci) handle_cmd_completion(xhci, &event->event_cmd); break; case TRB_PORT_STATUS: - handle_port_status(xhci, event); + handle_port_status(xhci, ir, event); update_ptrs = 0; break; case TRB_TRANSFER: - ret = handle_tx_event(xhci, &event->trans_event); + ret = handle_tx_event(xhci, ir, &event->trans_event); if (ret >= 0) update_ptrs = 0; break; @@ -2960,7 +2962,7 @@ static int xhci_handle_event(struct xhci_hcd *xhci) if (update_ptrs) /* Update SW event ring dequeue pointer */ - inc_deq(xhci, xhci->event_ring); + inc_deq(xhci, ir->event_ring); /* Are there more items on the event ring? Caller will call us again to * check. @@ -2974,16 +2976,17 @@ static int xhci_handle_event(struct xhci_hcd *xhci) * - To avoid "Event Ring Full Error" condition */ static void xhci_update_erst_dequeue(struct xhci_hcd *xhci, - union xhci_trb *event_ring_deq) + struct xhci_interrupter *ir, + union xhci_trb *event_ring_deq) { u64 temp_64; dma_addr_t deq; - temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + temp_64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); /* If necessary, update the HW's version of the event ring deq ptr. */ - if (event_ring_deq != xhci->event_ring->dequeue) { - deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, - xhci->event_ring->dequeue); + if (event_ring_deq != ir->event_ring->dequeue) { + deq = xhci_trb_virt_to_dma(ir->event_ring->deq_seg, + ir->event_ring->dequeue); if (deq == 0) xhci_warn(xhci, "WARN something wrong with SW event ring dequeue ptr\n"); /* @@ -3001,7 +3004,7 @@ static void xhci_update_erst_dequeue(struct xhci_hcd *xhci, /* Clear the event handler busy flag (RW1C) */ temp_64 |= ERST_EHB; - xhci_write_64(xhci, temp_64, &xhci->ir_set->erst_dequeue); + xhci_write_64(xhci, temp_64, &ir->ir_set->erst_dequeue); } /* @@ -3013,6 +3016,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); union xhci_trb *event_ring_deq; + struct xhci_interrupter *ir; irqreturn_t ret = IRQ_NONE; u64 temp_64; u32 status; @@ -3050,11 +3054,13 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) status |= STS_EINT; writel(status, &xhci->op_regs->status); + /* This is the handler of the primary interrupter */ + ir = xhci->interrupter; if (!hcd->msi_enabled) { u32 irq_pending; - irq_pending = readl(&xhci->ir_set->irq_pending); + irq_pending = readl(&ir->ir_set->irq_pending); irq_pending |= IMAN_IP; - writel(irq_pending, &xhci->ir_set->irq_pending); + writel(irq_pending, &ir->ir_set->irq_pending); } if (xhci->xhc_state & XHCI_STATE_DYING || @@ -3064,22 +3070,22 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) /* Clear the event handler busy flag (RW1C); * the event ring should be empty. */ - temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + temp_64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); xhci_write_64(xhci, temp_64 | ERST_EHB, - &xhci->ir_set->erst_dequeue); + &ir->ir_set->erst_dequeue); ret = IRQ_HANDLED; goto out; } - event_ring_deq = xhci->event_ring->dequeue; + event_ring_deq = ir->event_ring->dequeue; /* FIXME this should be a delayed service routine * that clears the EHB. */ - while (xhci_handle_event(xhci) > 0) { + while (xhci_handle_event(xhci, ir) > 0) { if (event_loop++ < TRBS_PER_SEGMENT / 2) continue; - xhci_update_erst_dequeue(xhci, event_ring_deq); - event_ring_deq = xhci->event_ring->dequeue; + xhci_update_erst_dequeue(xhci, ir, event_ring_deq); + event_ring_deq = ir->event_ring->dequeue; /* ring is half-full, force isoc trbs to interrupt more often */ if (xhci->isoc_bei_interval > AVOID_BEI_INTERVAL_MIN) @@ -3088,7 +3094,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) event_loop = 0; } - xhci_update_erst_dequeue(xhci, event_ring_deq); + xhci_update_erst_dequeue(xhci, ir, event_ring_deq); ret = IRQ_HANDLED; out: diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index f61fda4715cc..8dc3f2c00577 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -613,6 +613,7 @@ static int xhci_init(struct usb_hcd *hcd) static int xhci_run_finished(struct xhci_hcd *xhci) { + struct xhci_interrupter *ir = xhci->interrupter; unsigned long flags; u32 temp; @@ -628,8 +629,8 @@ static int xhci_run_finished(struct xhci_hcd *xhci) writel(temp, &xhci->op_regs->command); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Enable primary interrupter"); - temp = readl(&xhci->ir_set->irq_pending); - writel(ER_IRQ_ENABLE(temp), &xhci->ir_set->irq_pending); + temp = readl(&ir->ir_set->irq_pending); + writel(ER_IRQ_ENABLE(temp), &ir->ir_set->irq_pending); if (xhci_start(xhci)) { xhci_halt(xhci); @@ -665,7 +666,7 @@ int xhci_run(struct usb_hcd *hcd) u64 temp_64; int ret; struct xhci_hcd *xhci = hcd_to_xhci(hcd); - + struct xhci_interrupter *ir = xhci->interrupter; /* Start the xHCI host controller running only after the USB 2.0 roothub * is setup. */ @@ -680,17 +681,17 @@ int xhci_run(struct usb_hcd *hcd) if (ret) return ret; - temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + temp_64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); temp_64 &= ~ERST_PTR_MASK; xhci_dbg_trace(xhci, trace_xhci_dbg_init, "ERST deq = 64'h%0lx", (long unsigned int) temp_64); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Set the interrupt modulation register"); - temp = readl(&xhci->ir_set->irq_control); + temp = readl(&ir->ir_set->irq_control); temp &= ~ER_IRQ_INTERVAL_MASK; temp |= (xhci->imod_interval / 250) & ER_IRQ_INTERVAL_MASK; - writel(temp, &xhci->ir_set->irq_control); + writel(temp, &ir->ir_set->irq_control); if (xhci->quirks & XHCI_NEC_HOST) { struct xhci_command *command; @@ -769,8 +770,8 @@ static void xhci_stop(struct usb_hcd *hcd) "// Disabling event ring interrupts"); temp = readl(&xhci->op_regs->status); writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); - temp = readl(&xhci->ir_set->irq_pending); - writel(ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending); + temp = readl(&xhci->interrupter->ir_set->irq_pending); + writel(ER_IRQ_DISABLE(temp), &xhci->interrupter->ir_set->irq_pending); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "cleaning up memory"); xhci_mem_cleanup(xhci); @@ -832,28 +833,36 @@ EXPORT_SYMBOL_GPL(xhci_shutdown); #ifdef CONFIG_PM static void xhci_save_registers(struct xhci_hcd *xhci) { + struct xhci_interrupter *ir = xhci->interrupter; + xhci->s3.command = readl(&xhci->op_regs->command); xhci->s3.dev_nt = readl(&xhci->op_regs->dev_notification); xhci->s3.dcbaa_ptr = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr); xhci->s3.config_reg = readl(&xhci->op_regs->config_reg); - xhci->s3.erst_size = readl(&xhci->ir_set->erst_size); - xhci->s3.erst_base = xhci_read_64(xhci, &xhci->ir_set->erst_base); - xhci->s3.erst_dequeue = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); - xhci->s3.irq_pending = readl(&xhci->ir_set->irq_pending); - xhci->s3.irq_control = readl(&xhci->ir_set->irq_control); + + if (!ir) + return; + + ir->s3_erst_size = readl(&ir->ir_set->erst_size); + ir->s3_erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base); + ir->s3_erst_dequeue = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); + ir->s3_irq_pending = readl(&ir->ir_set->irq_pending); + ir->s3_irq_control = readl(&ir->ir_set->irq_control); } static void xhci_restore_registers(struct xhci_hcd *xhci) { + struct xhci_interrupter *ir = xhci->interrupter; + writel(xhci->s3.command, &xhci->op_regs->command); writel(xhci->s3.dev_nt, &xhci->op_regs->dev_notification); xhci_write_64(xhci, xhci->s3.dcbaa_ptr, &xhci->op_regs->dcbaa_ptr); writel(xhci->s3.config_reg, &xhci->op_regs->config_reg); - writel(xhci->s3.erst_size, &xhci->ir_set->erst_size); - xhci_write_64(xhci, xhci->s3.erst_base, &xhci->ir_set->erst_base); - xhci_write_64(xhci, xhci->s3.erst_dequeue, &xhci->ir_set->erst_dequeue); - writel(xhci->s3.irq_pending, &xhci->ir_set->irq_pending); - writel(xhci->s3.irq_control, &xhci->ir_set->irq_control); + writel(ir->s3_erst_size, &ir->ir_set->erst_size); + xhci_write_64(xhci, ir->s3_erst_base, &ir->ir_set->erst_base); + xhci_write_64(xhci, ir->s3_erst_dequeue, &ir->ir_set->erst_dequeue); + writel(ir->s3_irq_pending, &ir->ir_set->irq_pending); + writel(ir->s3_irq_control, &ir->ir_set->irq_control); } static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci) @@ -1218,8 +1227,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) xhci_dbg(xhci, "// Disabling event ring interrupts\n"); temp = readl(&xhci->op_regs->status); writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); - temp = readl(&xhci->ir_set->irq_pending); - writel(ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending); + temp = readl(&xhci->interrupter->ir_set->irq_pending); + writel(ER_IRQ_DISABLE(temp), &xhci->interrupter->ir_set->irq_pending); xhci_dbg(xhci, "cleaning up memory\n"); xhci_mem_cleanup(xhci); @@ -5334,6 +5343,11 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) if (xhci->hci_version > 0x100) xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2); + /* xhci-plat or xhci-pci might have set max_interrupters already */ + if ((!xhci->max_interrupters) || + xhci->max_interrupters > HCS_MAX_INTRS(xhci->hcs_params1)) + xhci->max_interrupters = HCS_MAX_INTRS(xhci->hcs_params1); + xhci->quirks |= quirks; get_quirks(dev, xhci); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 0dd92f9089fe..95eb235d1f70 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1687,11 +1687,6 @@ struct s3_save { u32 dev_nt; u64 dcbaa_ptr; u32 config_reg; - u32 irq_pending; - u32 irq_control; - u32 erst_size; - u64 erst_base; - u64 erst_dequeue; }; /* Use for lpm */ @@ -1718,7 +1713,18 @@ struct xhci_bus_state { struct completion u3exit_done[USB_MAXCHILDREN]; }; - +struct xhci_interrupter { + struct xhci_ring *event_ring; + struct xhci_erst erst; + struct xhci_intr_reg __iomem *ir_set; + unsigned int intr_num; + /* For interrupter registers save and restore over suspend/resume */ + u32 s3_irq_pending; + u32 s3_irq_control; + u32 s3_erst_size; + u64 s3_erst_base; + u64 s3_erst_dequeue; +}; /* * It can take up to 20 ms to transition from RExit to U0 on the * Intel Lynx Point LP xHCI host. @@ -1761,8 +1767,6 @@ struct xhci_hcd { struct xhci_op_regs __iomem *op_regs; struct xhci_run_regs __iomem *run_regs; struct xhci_doorbell_array __iomem *dba; - /* Our HCD's current interrupter register set */ - struct xhci_intr_reg __iomem *ir_set; /* Cached register copies of read-only HC data */ __u32 hcs_params1; @@ -1797,6 +1801,7 @@ struct xhci_hcd { struct reset_control *reset; /* data structures */ struct xhci_device_context_array *dcbaa; + struct xhci_interrupter *interrupter; struct xhci_ring *cmd_ring; unsigned int cmd_ring_state; #define CMD_RING_STATE_RUNNING (1 << 0) @@ -1807,8 +1812,7 @@ struct xhci_hcd { struct delayed_work cmd_timer; struct completion cmd_ring_stop_completion; struct xhci_command *current_cmd; - struct xhci_ring *event_ring; - struct xhci_erst erst; + /* Scratchpad */ struct xhci_scratchpad *scratchpad; -- cgit v1.2.3 From 52dd0483e822d097fd6f522af2438a9b6f6eb0a9 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Thu, 2 Feb 2023 17:04:58 +0200 Subject: xhci: add helpers for enabling and disabling interrupters Simple helpers to set and clear the IE (interrupter enable) bit for an interrupter. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-5-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 8dc3f2c00577..6183ce8574b1 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -292,6 +292,32 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci) xhci_info(xhci, "Fault detected\n"); } +static int xhci_enable_interrupter(struct xhci_interrupter *ir) +{ + u32 iman; + + if (!ir || !ir->ir_set) + return -EINVAL; + + iman = readl(&ir->ir_set->irq_pending); + writel(ER_IRQ_ENABLE(iman), &ir->ir_set->irq_pending); + + return 0; +} + +static int xhci_disable_interrupter(struct xhci_interrupter *ir) +{ + u32 iman; + + if (!ir || !ir->ir_set) + return -EINVAL; + + iman = readl(&ir->ir_set->irq_pending); + writel(ER_IRQ_DISABLE(iman), &ir->ir_set->irq_pending); + + return 0; +} + #ifdef CONFIG_USB_PCI /* * Set up MSI @@ -610,7 +636,6 @@ static int xhci_init(struct usb_hcd *hcd) /*-------------------------------------------------------------------------*/ - static int xhci_run_finished(struct xhci_hcd *xhci) { struct xhci_interrupter *ir = xhci->interrupter; @@ -629,8 +654,7 @@ static int xhci_run_finished(struct xhci_hcd *xhci) writel(temp, &xhci->op_regs->command); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Enable primary interrupter"); - temp = readl(&ir->ir_set->irq_pending); - writel(ER_IRQ_ENABLE(temp), &ir->ir_set->irq_pending); + xhci_enable_interrupter(ir); if (xhci_start(xhci)) { xhci_halt(xhci); @@ -734,6 +758,7 @@ static void xhci_stop(struct usb_hcd *hcd) { u32 temp; struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct xhci_interrupter *ir = xhci->interrupter; mutex_lock(&xhci->mutex); @@ -770,8 +795,7 @@ static void xhci_stop(struct usb_hcd *hcd) "// Disabling event ring interrupts"); temp = readl(&xhci->op_regs->status); writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); - temp = readl(&xhci->interrupter->ir_set->irq_pending); - writel(ER_IRQ_DISABLE(temp), &xhci->interrupter->ir_set->irq_pending); + xhci_disable_interrupter(ir); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "cleaning up memory"); xhci_mem_cleanup(xhci); @@ -1227,8 +1251,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) xhci_dbg(xhci, "// Disabling event ring interrupts\n"); temp = readl(&xhci->op_regs->status); writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); - temp = readl(&xhci->interrupter->ir_set->irq_pending); - writel(ER_IRQ_DISABLE(temp), &xhci->interrupter->ir_set->irq_pending); + xhci_disable_interrupter(xhci->interrupter); xhci_dbg(xhci, "cleaning up memory\n"); xhci_mem_cleanup(xhci); -- cgit v1.2.3 From faaae0190dcd1e230616c85bbc3b339f27ba5b81 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Thu, 2 Feb 2023 17:04:59 +0200 Subject: xhci: cleanup xhci_hub_control port references Both port number and port structure of a port are referred to several times when handing hub requests in xhci. Use more suitable data types and readable names for these. Cleanup only, no functional changes Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-6-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 123 +++++++++++++++++++++++--------------------- 1 file changed, 63 insertions(+), 60 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 7750a5eed435..181c070d6a99 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -1209,11 +1209,14 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 test_mode = 0; struct xhci_hub *rhub; struct xhci_port **ports; + struct xhci_port *port; + int portnum1; rhub = xhci_get_rhub(hcd); ports = rhub->ports; max_ports = rhub->num_ports; bus_state = &rhub->bus_state; + portnum1 = wIndex & 0xff; spin_lock_irqsave(&xhci->lock, flags); switch (typeReq) { @@ -1247,10 +1250,12 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, spin_unlock_irqrestore(&xhci->lock, flags); return retval; case GetPortStatus: - if (!wIndex || wIndex > max_ports) + if (!portnum1 || portnum1 > max_ports) goto error; + wIndex--; - temp = readl(ports[wIndex]->addr); + port = ports[portnum1 - 1]; + temp = readl(port->addr); if (temp == ~(u32)0) { xhci_hc_died(xhci); retval = -ENODEV; @@ -1263,7 +1268,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, goto error; xhci_dbg(xhci, "Get port status %d-%d read: 0x%x, return 0x%x", - hcd->self.busnum, wIndex + 1, temp, status); + hcd->self.busnum, portnum1, temp, status); put_unaligned(cpu_to_le32(status), (__le32 *) buf); /* if USB 3.1 extended port status return additional 4 bytes */ @@ -1275,7 +1280,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, retval = -EINVAL; break; } - port_li = readl(ports[wIndex]->addr + PORTLI); + port_li = readl(port->addr + PORTLI); status = xhci_get_ext_port_status(temp, port_li); put_unaligned_le32(status, &buf[4]); } @@ -1289,11 +1294,14 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, test_mode = (wIndex & 0xff00) >> 8; /* The MSB of wIndex is the U1/U2 timeout */ timeout = (wIndex & 0xff00) >> 8; + wIndex &= 0xff; - if (!wIndex || wIndex > max_ports) + if (!portnum1 || portnum1 > max_ports) goto error; + + port = ports[portnum1 - 1]; wIndex--; - temp = readl(ports[wIndex]->addr); + temp = readl(port->addr); if (temp == ~(u32)0) { xhci_hc_died(xhci); retval = -ENODEV; @@ -1303,11 +1311,10 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, /* FIXME: What new port features do we need to support? */ switch (wValue) { case USB_PORT_FEAT_SUSPEND: - temp = readl(ports[wIndex]->addr); + temp = readl(port->addr); if ((temp & PORT_PLS_MASK) != XDEV_U0) { /* Resume the port to U0 first */ - xhci_set_link_state(xhci, ports[wIndex], - XDEV_U0); + xhci_set_link_state(xhci, port, XDEV_U0); spin_unlock_irqrestore(&xhci->lock, flags); msleep(10); spin_lock_irqsave(&xhci->lock, flags); @@ -1316,16 +1323,16 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, * a port unless the port reports that it is in the * enabled (PED = ‘1’,PLS < ‘3’) state. */ - temp = readl(ports[wIndex]->addr); + temp = readl(port->addr); if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) || (temp & PORT_PLS_MASK) >= XDEV_U3) { xhci_warn(xhci, "USB core suspending port %d-%d not in U0/U1/U2\n", - hcd->self.busnum, wIndex + 1); + hcd->self.busnum, portnum1); goto error; } slot_id = xhci_find_slot_id_by_port(hcd, xhci, - wIndex + 1); + portnum1); if (!slot_id) { xhci_warn(xhci, "slot_id is zero\n"); goto error; @@ -1335,21 +1342,21 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_stop_device(xhci, slot_id, 1); spin_lock_irqsave(&xhci->lock, flags); - xhci_set_link_state(xhci, ports[wIndex], XDEV_U3); + xhci_set_link_state(xhci, port, XDEV_U3); spin_unlock_irqrestore(&xhci->lock, flags); msleep(10); /* wait device to enter */ spin_lock_irqsave(&xhci->lock, flags); - temp = readl(ports[wIndex]->addr); + temp = readl(port->addr); bus_state->suspended_ports |= 1 << wIndex; break; case USB_PORT_FEAT_LINK_STATE: - temp = readl(ports[wIndex]->addr); + temp = readl(port->addr); /* Disable port */ if (link_state == USB_SS_PORT_LS_SS_DISABLED) { xhci_dbg(xhci, "Disable port %d-%d\n", - hcd->self.busnum, wIndex + 1); + hcd->self.busnum, portnum1); temp = xhci_port_state_to_neutral(temp); /* * Clear all change bits, so that we get a new @@ -1358,18 +1365,17 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp |= PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | PORT_RC | PORT_PLC | PORT_CEC; - writel(temp | PORT_PE, ports[wIndex]->addr); - temp = readl(ports[wIndex]->addr); + writel(temp | PORT_PE, port->addr); + temp = readl(port->addr); break; } /* Put link in RxDetect (enable port) */ if (link_state == USB_SS_PORT_LS_RX_DETECT) { xhci_dbg(xhci, "Enable port %d-%d\n", - hcd->self.busnum, wIndex + 1); - xhci_set_link_state(xhci, ports[wIndex], - link_state); - temp = readl(ports[wIndex]->addr); + hcd->self.busnum, portnum1); + xhci_set_link_state(xhci, port, link_state); + temp = readl(port->addr); break; } @@ -1399,11 +1405,10 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, } xhci_dbg(xhci, "Enable compliance mode transition for port %d-%d\n", - hcd->self.busnum, wIndex + 1); - xhci_set_link_state(xhci, ports[wIndex], - link_state); + hcd->self.busnum, portnum1); + xhci_set_link_state(xhci, port, link_state); - temp = readl(ports[wIndex]->addr); + temp = readl(port->addr); break; } /* Port must be enabled */ @@ -1414,8 +1419,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, /* Can't set port link state above '3' (U3) */ if (link_state > USB_SS_PORT_LS_U3) { xhci_warn(xhci, "Cannot set port %d-%d link state %d\n", - hcd->self.busnum, wIndex + 1, - link_state); + hcd->self.busnum, portnum1, link_state); goto error; } @@ -1440,8 +1444,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, reinit_completion(&bus_state->u3exit_done[wIndex]); } if (pls <= XDEV_U3) /* U1, U2, U3 */ - xhci_set_link_state(xhci, ports[wIndex], - USB_SS_PORT_LS_U0); + xhci_set_link_state(xhci, port, USB_SS_PORT_LS_U0); if (!wait_u0) { if (pls > XDEV_U3) goto error; @@ -1451,16 +1454,16 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if (!wait_for_completion_timeout(&bus_state->u3exit_done[wIndex], msecs_to_jiffies(500))) xhci_dbg(xhci, "missing U0 port change event for port %d-%d\n", - hcd->self.busnum, wIndex + 1); + hcd->self.busnum, portnum1); spin_lock_irqsave(&xhci->lock, flags); - temp = readl(ports[wIndex]->addr); + temp = readl(port->addr); break; } if (link_state == USB_SS_PORT_LS_U3) { int retries = 16; slot_id = xhci_find_slot_id_by_port(hcd, xhci, - wIndex + 1); + portnum1); if (slot_id) { /* unlock to execute stop endpoint * commands */ @@ -1469,16 +1472,16 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_stop_device(xhci, slot_id, 1); spin_lock_irqsave(&xhci->lock, flags); } - xhci_set_link_state(xhci, ports[wIndex], USB_SS_PORT_LS_U3); + xhci_set_link_state(xhci, port, USB_SS_PORT_LS_U3); spin_unlock_irqrestore(&xhci->lock, flags); while (retries--) { usleep_range(4000, 8000); - temp = readl(ports[wIndex]->addr); + temp = readl(port->addr); if ((temp & PORT_PLS_MASK) == XDEV_U3) break; } spin_lock_irqsave(&xhci->lock, flags); - temp = readl(ports[wIndex]->addr); + temp = readl(port->addr); bus_state->suspended_ports |= 1 << wIndex; } break; @@ -1493,39 +1496,38 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, break; case USB_PORT_FEAT_RESET: temp = (temp | PORT_RESET); - writel(temp, ports[wIndex]->addr); + writel(temp, port->addr); - temp = readl(ports[wIndex]->addr); + temp = readl(port->addr); xhci_dbg(xhci, "set port reset, actual port %d-%d status = 0x%x\n", - hcd->self.busnum, wIndex + 1, temp); + hcd->self.busnum, portnum1, temp); break; case USB_PORT_FEAT_REMOTE_WAKE_MASK: - xhci_set_remote_wake_mask(xhci, ports[wIndex], - wake_mask); - temp = readl(ports[wIndex]->addr); + xhci_set_remote_wake_mask(xhci, port, wake_mask); + temp = readl(port->addr); xhci_dbg(xhci, "set port remote wake mask, actual port %d-%d status = 0x%x\n", - hcd->self.busnum, wIndex + 1, temp); + hcd->self.busnum, portnum1, temp); break; case USB_PORT_FEAT_BH_PORT_RESET: temp |= PORT_WR; - writel(temp, ports[wIndex]->addr); - temp = readl(ports[wIndex]->addr); + writel(temp, port->addr); + temp = readl(port->addr); break; case USB_PORT_FEAT_U1_TIMEOUT: if (hcd->speed < HCD_USB3) goto error; - temp = readl(ports[wIndex]->addr + PORTPMSC); + temp = readl(port->addr + PORTPMSC); temp &= ~PORT_U1_TIMEOUT_MASK; temp |= PORT_U1_TIMEOUT(timeout); - writel(temp, ports[wIndex]->addr + PORTPMSC); + writel(temp, port->addr + PORTPMSC); break; case USB_PORT_FEAT_U2_TIMEOUT: if (hcd->speed < HCD_USB3) goto error; - temp = readl(ports[wIndex]->addr + PORTPMSC); + temp = readl(port->addr + PORTPMSC); temp &= ~PORT_U2_TIMEOUT_MASK; temp |= PORT_U2_TIMEOUT(timeout); - writel(temp, ports[wIndex]->addr + PORTPMSC); + writel(temp, port->addr + PORTPMSC); break; case USB_PORT_FEAT_TEST: /* 4.19.6 Port Test Modes (USB2 Test Mode) */ @@ -1541,13 +1543,16 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, goto error; } /* unblock any posted writes */ - temp = readl(ports[wIndex]->addr); + temp = readl(port->addr); break; case ClearPortFeature: - if (!wIndex || wIndex > max_ports) + if (!portnum1 || portnum1 > max_ports) goto error; + + port = ports[portnum1 - 1]; + wIndex--; - temp = readl(ports[wIndex]->addr); + temp = readl(port->addr); if (temp == ~(u32)0) { xhci_hc_died(xhci); retval = -ENODEV; @@ -1557,7 +1562,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp = xhci_port_state_to_neutral(temp); switch (wValue) { case USB_PORT_FEAT_SUSPEND: - temp = readl(ports[wIndex]->addr); + temp = readl(port->addr); xhci_dbg(xhci, "clear USB_PORT_FEAT_SUSPEND\n"); xhci_dbg(xhci, "PORTSC %04x\n", temp); if (temp & PORT_RESET) @@ -1568,20 +1573,18 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, set_bit(wIndex, &bus_state->resuming_ports); usb_hcd_start_port_resume(&hcd->self, wIndex); - xhci_set_link_state(xhci, ports[wIndex], - XDEV_RESUME); + xhci_set_link_state(xhci, port, XDEV_RESUME); spin_unlock_irqrestore(&xhci->lock, flags); msleep(USB_RESUME_TIMEOUT); spin_lock_irqsave(&xhci->lock, flags); - xhci_set_link_state(xhci, ports[wIndex], - XDEV_U0); + xhci_set_link_state(xhci, port, XDEV_U0); clear_bit(wIndex, &bus_state->resuming_ports); usb_hcd_end_port_resume(&hcd->self, wIndex); } bus_state->port_c_suspend |= 1 << wIndex; slot_id = xhci_find_slot_id_by_port(hcd, xhci, - wIndex + 1); + portnum1); if (!slot_id) { xhci_dbg(xhci, "slot_id is zero\n"); goto error; @@ -1599,11 +1602,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case USB_PORT_FEAT_C_PORT_LINK_STATE: case USB_PORT_FEAT_C_PORT_CONFIG_ERROR: xhci_clear_port_change_bit(xhci, wValue, wIndex, - ports[wIndex]->addr, temp); + port->addr, temp); break; case USB_PORT_FEAT_ENABLE: xhci_disable_port(hcd, xhci, wIndex, - ports[wIndex]->addr, temp); + port->addr, temp); break; case USB_PORT_FEAT_POWER: xhci_set_port_power(xhci, hcd, wIndex, false, &flags); -- cgit v1.2.3 From a66095a957ce3ce2a5154f7981845942f26e477d Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Thu, 2 Feb 2023 17:05:00 +0200 Subject: xhci: pass port pointer as parameter to xhci_set_port_power() Pass the port structure pointer directly to xhci_set_port_power() instead of hcd and port index. cleanup Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-7-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 181c070d6a99..238d05206d2c 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -666,20 +666,18 @@ struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd) * It will release and re-aquire the lock while calling ACPI * method. */ -static void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd, - u16 index, bool on, unsigned long *flags) +static void xhci_set_port_power(struct xhci_hcd *xhci, struct xhci_port *port, + bool on, unsigned long *flags) __must_hold(&xhci->lock) { - struct xhci_hub *rhub; - struct xhci_port *port; + struct usb_hcd *hcd; u32 temp; - rhub = xhci_get_rhub(hcd); - port = rhub->ports[index]; + hcd = port->rhub->hcd; temp = readl(port->addr); xhci_dbg(xhci, "set port power %d-%d %s, portsc: 0x%x\n", - hcd->self.busnum, index + 1, on ? "ON" : "OFF", temp); + hcd->self.busnum, port->hcd_portnum + 1, on ? "ON" : "OFF", temp); temp = xhci_port_state_to_neutral(temp); @@ -694,10 +692,10 @@ static void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd, spin_unlock_irqrestore(&xhci->lock, *flags); temp = usb_acpi_power_manageable(hcd->self.root_hub, - index); + port->hcd_portnum); if (temp) usb_acpi_set_power_state(hcd->self.root_hub, - index, on); + port->hcd_portnum, on); spin_lock_irqsave(&xhci->lock, *flags); } @@ -721,7 +719,6 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci, u16 test_mode, u16 wIndex, unsigned long *flags) __must_hold(&xhci->lock) { - struct usb_hcd *usb3_hcd = xhci_get_usb3_hcd(xhci); int i, retval; /* Disable all Device Slots */ @@ -742,10 +739,10 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci, xhci_dbg(xhci, "Disable all port (PP = 0)\n"); /* Power off USB3 ports*/ for (i = 0; i < xhci->usb3_rhub.num_ports; i++) - xhci_set_port_power(xhci, usb3_hcd, i, false, flags); + xhci_set_port_power(xhci, xhci->usb3_rhub.ports[i], false, flags); /* Power off USB2 ports*/ for (i = 0; i < xhci->usb2_rhub.num_ports; i++) - xhci_set_port_power(xhci, xhci->main_hcd, i, false, flags); + xhci_set_port_power(xhci, xhci->usb2_rhub.ports[i], false, flags); /* Stop the controller */ xhci_dbg(xhci, "Stop controller\n"); retval = xhci_halt(xhci); @@ -1492,7 +1489,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, * However, hub_wq will ignore the roothub events until * the roothub is registered. */ - xhci_set_port_power(xhci, hcd, wIndex, true, &flags); + xhci_set_port_power(xhci, port, true, &flags); break; case USB_PORT_FEAT_RESET: temp = (temp | PORT_RESET); @@ -1609,7 +1606,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, port->addr, temp); break; case USB_PORT_FEAT_POWER: - xhci_set_port_power(xhci, hcd, wIndex, false, &flags); + xhci_set_port_power(xhci, port, false, &flags); break; case USB_PORT_FEAT_TEST: retval = xhci_exit_test_mode(xhci); -- cgit v1.2.3 From 2996e9fc00c378987c18ecbafe5624581b18c0d6 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Thu, 2 Feb 2023 17:05:01 +0200 Subject: xhci: move port specific items such as state completions to port structure Now that we have a port structure for each port it makes sense to move per port variables, timestamps and completions there. Get rid of storing bitfileds and arrays of port specific items per bus. Move unsigned long resume_done; insigned long rexit_ports struct completion rexit_done; struct completion u3exit_done; Rename rexit_ports to rexit_active, and remove a redundant hcd speed check while checking if rexit_active is set. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-8-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 31 +++++++++++++++---------------- drivers/usb/host/xhci-mem.c | 10 +++------- drivers/usb/host/xhci-ring.c | 13 ++++++------- drivers/usb/host/xhci.h | 9 ++++----- 4 files changed, 28 insertions(+), 35 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 238d05206d2c..75c9609f32f0 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -936,7 +936,7 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, return -EINVAL; } /* did port event handler already start resume timing? */ - if (!bus_state->resume_done[wIndex]) { + if (!port->resume_done) { /* If not, maybe we are in a host initated resume? */ if (test_bit(wIndex, &bus_state->resuming_ports)) { /* Host initated resume doesn't time the resume @@ -953,28 +953,27 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, msecs_to_jiffies(USB_RESUME_TIMEOUT); set_bit(wIndex, &bus_state->resuming_ports); - bus_state->resume_done[wIndex] = timeout; + port->resume_done = timeout; mod_timer(&hcd->rh_timer, timeout); usb_hcd_start_port_resume(&hcd->self, wIndex); } /* Has resume been signalled for USB_RESUME_TIME yet? */ - } else if (time_after_eq(jiffies, bus_state->resume_done[wIndex])) { + } else if (time_after_eq(jiffies, port->resume_done)) { int time_left; xhci_dbg(xhci, "resume USB2 port %d-%d\n", hcd->self.busnum, wIndex + 1); - bus_state->resume_done[wIndex] = 0; + port->resume_done = 0; clear_bit(wIndex, &bus_state->resuming_ports); - - set_bit(wIndex, &bus_state->rexit_ports); + port->rexit_active = true; xhci_test_and_clear_bit(xhci, port, PORT_PLC); xhci_set_link_state(xhci, port, XDEV_U0); spin_unlock_irqrestore(&xhci->lock, *flags); time_left = wait_for_completion_timeout( - &bus_state->rexit_done[wIndex], + &port->rexit_done, msecs_to_jiffies(XHCI_MAX_REXIT_TIMEOUT_MS)); spin_lock_irqsave(&xhci->lock, *flags); @@ -993,7 +992,7 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, xhci_warn(xhci, "Port resume timed out, port %d-%d: 0x%x\n", hcd->self.busnum, wIndex + 1, port_status); *status |= USB_PORT_STAT_SUSPEND; - clear_bit(wIndex, &bus_state->rexit_ports); + port->rexit_active = false; } usb_hcd_end_port_resume(&hcd->self, wIndex); @@ -1100,10 +1099,10 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, if (link_state == XDEV_U2) *status |= USB_PORT_STAT_L1; if (link_state == XDEV_U0) { - if (bus_state->resume_done[portnum]) + if (port->resume_done) usb_hcd_end_port_resume(&port->rhub->hcd->self, portnum); - bus_state->resume_done[portnum] = 0; + port->resume_done = 0; clear_bit(portnum, &bus_state->resuming_ports); if (bus_state->suspended_ports & (1 << portnum)) { bus_state->suspended_ports &= ~(1 << portnum); @@ -1175,11 +1174,11 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, * Clear stale usb2 resume signalling variables in case port changed * state during resume signalling. For example on error */ - if ((bus_state->resume_done[wIndex] || + if ((port->resume_done || test_bit(wIndex, &bus_state->resuming_ports)) && (raw_port_status & PORT_PLS_MASK) != XDEV_U3 && (raw_port_status & PORT_PLS_MASK) != XDEV_RESUME) { - bus_state->resume_done[wIndex] = 0; + port->resume_done = 0; clear_bit(wIndex, &bus_state->resuming_ports); usb_hcd_end_port_resume(&hcd->self, wIndex); } @@ -1438,7 +1437,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, pls == XDEV_RESUME || pls == XDEV_RECOVERY) { wait_u0 = true; - reinit_completion(&bus_state->u3exit_done[wIndex]); + reinit_completion(&port->u3exit_done); } if (pls <= XDEV_U3) /* U1, U2, U3 */ xhci_set_link_state(xhci, port, USB_SS_PORT_LS_U0); @@ -1448,7 +1447,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, break; } spin_unlock_irqrestore(&xhci->lock, flags); - if (!wait_for_completion_timeout(&bus_state->u3exit_done[wIndex], + if (!wait_for_completion_timeout(&port->u3exit_done, msecs_to_jiffies(500))) xhci_dbg(xhci, "missing U0 port change event for port %d-%d\n", hcd->self.busnum, portnum1); @@ -1688,8 +1687,8 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) if ((temp & mask) != 0 || (bus_state->port_c_suspend & 1 << i) || - (bus_state->resume_done[i] && time_after_eq( - jiffies, bus_state->resume_done[i]))) { + (ports[i]->resume_done && time_after_eq( + jiffies, ports[i]->resume_done))) { buf[(i + 1) / 8] |= 1 << (i + 1) % 8; status = 1; } diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 6b83c5c35cf8..d0a9467aa5fc 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -2154,6 +2154,9 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) xhci->hw_ports[i].addr = &xhci->op_regs->port_status_base + NUM_PORT_REGS * i; xhci->hw_ports[i].hw_portnum = i; + + init_completion(&xhci->hw_ports[i].rexit_done); + init_completion(&xhci->hw_ports[i].u3exit_done); } xhci->rh_bw = kcalloc_node(num_ports, sizeof(*xhci->rh_bw), flags, @@ -2439,13 +2442,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) */ for (i = 0; i < MAX_HC_SLOTS; i++) xhci->devs[i] = NULL; - for (i = 0; i < USB_MAXCHILDREN; i++) { - xhci->usb2_rhub.bus_state.resume_done[i] = 0; - xhci->usb3_rhub.bus_state.resume_done[i] = 0; - /* Only the USB 2.0 completions will ever be used. */ - init_completion(&xhci->usb2_rhub.bus_state.rexit_done[i]); - init_completion(&xhci->usb3_rhub.bus_state.u3exit_done[i]); - } if (scratchpad_alloc(xhci, flags)) goto fail; diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 451d48b87cf7..611580d4adad 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1924,7 +1924,7 @@ static void handle_port_status(struct xhci_hcd *xhci, goto cleanup; } else if (!test_bit(hcd_portnum, &bus_state->resuming_ports)) { xhci_dbg(xhci, "resume HS port %d\n", port_id); - bus_state->resume_done[hcd_portnum] = jiffies + + port->resume_done = jiffies + msecs_to_jiffies(USB_RESUME_TIMEOUT); set_bit(hcd_portnum, &bus_state->resuming_ports); /* Do the rest in GetPortStatus after resume time delay. @@ -1933,7 +1933,7 @@ static void handle_port_status(struct xhci_hcd *xhci, */ set_bit(HCD_FLAG_POLL_RH, &hcd->flags); mod_timer(&hcd->rh_timer, - bus_state->resume_done[hcd_portnum]); + port->resume_done); usb_hcd_start_port_resume(&hcd->self, hcd_portnum); bogus_port_status = true; } @@ -1945,7 +1945,7 @@ static void handle_port_status(struct xhci_hcd *xhci, (portsc & PORT_PLS_MASK) == XDEV_U1 || (portsc & PORT_PLS_MASK) == XDEV_U2)) { xhci_dbg(xhci, "resume SS port %d finished\n", port_id); - complete(&bus_state->u3exit_done[hcd_portnum]); + complete(&port->u3exit_done); /* We've just brought the device into U0/1/2 through either the * Resume state after a device remote wakeup, or through the * U3Exit state after a host-initiated resume. If it's a device @@ -1970,10 +1970,9 @@ static void handle_port_status(struct xhci_hcd *xhci, * RExit to a disconnect state). If so, let the driver know it's * out of the RExit state. */ - if (!DEV_SUPERSPEED_ANY(portsc) && hcd->speed < HCD_USB3 && - test_and_clear_bit(hcd_portnum, - &bus_state->rexit_ports)) { - complete(&bus_state->rexit_done[hcd_portnum]); + if (hcd->speed < HCD_USB3 && port->rexit_active) { + complete(&port->rexit_done); + port->rexit_active = false; bogus_port_status = true; goto cleanup; } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 95eb235d1f70..578e219292fd 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1704,13 +1704,8 @@ struct xhci_bus_state { u32 port_c_suspend; u32 suspended_ports; u32 port_remote_wakeup; - unsigned long resume_done[USB_MAXCHILDREN]; /* which ports have started to resume */ unsigned long resuming_ports; - /* Which ports are waiting on RExit to U0 transition. */ - unsigned long rexit_ports; - struct completion rexit_done[USB_MAXCHILDREN]; - struct completion u3exit_done[USB_MAXCHILDREN]; }; struct xhci_interrupter { @@ -1745,6 +1740,10 @@ struct xhci_port { struct xhci_hub *rhub; struct xhci_port_cap *port_cap; unsigned int lpm_incapable:1; + unsigned long resume_done; + bool rexit_active; + struct completion rexit_done; + struct completion u3exit_done; }; struct xhci_hub { -- cgit v1.2.3 From 6baf7e749ab3aa4fcfeef3a26e8ec2306572cd05 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Thu, 2 Feb 2023 17:05:02 +0200 Subject: xhci: Pass port structure as parameter to xhci_disable_port(). Pass the port structure to xhci_disable_port() instead of address, index, and value. re-read the port portsc value before disabling the port. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-9-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 75c9609f32f0..b27969e3cdcf 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -578,13 +578,16 @@ void xhci_ring_device(struct xhci_hcd *xhci, int slot_id) return; } -static void xhci_disable_port(struct usb_hcd *hcd, struct xhci_hcd *xhci, - u16 wIndex, __le32 __iomem *addr, u32 port_status) +static void xhci_disable_port(struct xhci_hcd *xhci, struct xhci_port *port) { + struct usb_hcd *hcd; + u32 portsc; + + hcd = port->rhub->hcd; + /* Don't allow the USB core to disable SuperSpeed ports. */ if (hcd->speed >= HCD_USB3) { - xhci_dbg(xhci, "Ignoring request to disable " - "SuperSpeed port.\n"); + xhci_dbg(xhci, "Ignoring request to disable SuperSpeed port.\n"); return; } @@ -594,11 +597,15 @@ static void xhci_disable_port(struct usb_hcd *hcd, struct xhci_hcd *xhci, return; } + portsc = readl(port->addr); + portsc = xhci_port_state_to_neutral(portsc); + /* Write 1 to disable the port */ - writel(port_status | PORT_PE, addr); - port_status = readl(addr); + writel(portsc | PORT_PE, port->addr); + + portsc = readl(port->addr); xhci_dbg(xhci, "disable port %d-%d, portsc: 0x%x\n", - hcd->self.busnum, wIndex + 1, port_status); + hcd->self.busnum, port->hcd_portnum + 1, portsc); } static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, @@ -1601,8 +1608,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, port->addr, temp); break; case USB_PORT_FEAT_ENABLE: - xhci_disable_port(hcd, xhci, wIndex, - port->addr, temp); + xhci_disable_port(xhci, port); break; case USB_PORT_FEAT_POWER: xhci_set_port_power(xhci, port, false, &flags); -- cgit v1.2.3 From a909d629ae77b97b6288bc3cfe68560454bf79c6 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Thu, 2 Feb 2023 17:05:03 +0200 Subject: xhci: rename resume_done to resume_timestamp resume_done is just a timestamp, avoid confusing it with completions related to port state transitions that are named *_done Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-10-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 20 ++++++++++---------- drivers/usb/host/xhci-ring.c | 4 ++-- drivers/usb/host/xhci.h | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index b27969e3cdcf..a2053aa9b4a9 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -943,7 +943,7 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, return -EINVAL; } /* did port event handler already start resume timing? */ - if (!port->resume_done) { + if (!port->resume_timestamp) { /* If not, maybe we are in a host initated resume? */ if (test_bit(wIndex, &bus_state->resuming_ports)) { /* Host initated resume doesn't time the resume @@ -960,18 +960,18 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, msecs_to_jiffies(USB_RESUME_TIMEOUT); set_bit(wIndex, &bus_state->resuming_ports); - port->resume_done = timeout; + port->resume_timestamp = timeout; mod_timer(&hcd->rh_timer, timeout); usb_hcd_start_port_resume(&hcd->self, wIndex); } /* Has resume been signalled for USB_RESUME_TIME yet? */ - } else if (time_after_eq(jiffies, port->resume_done)) { + } else if (time_after_eq(jiffies, port->resume_timestamp)) { int time_left; xhci_dbg(xhci, "resume USB2 port %d-%d\n", hcd->self.busnum, wIndex + 1); - port->resume_done = 0; + port->resume_timestamp = 0; clear_bit(wIndex, &bus_state->resuming_ports); port->rexit_active = true; @@ -1106,10 +1106,10 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, if (link_state == XDEV_U2) *status |= USB_PORT_STAT_L1; if (link_state == XDEV_U0) { - if (port->resume_done) + if (port->resume_timestamp) usb_hcd_end_port_resume(&port->rhub->hcd->self, portnum); - port->resume_done = 0; + port->resume_timestamp = 0; clear_bit(portnum, &bus_state->resuming_ports); if (bus_state->suspended_ports & (1 << portnum)) { bus_state->suspended_ports &= ~(1 << portnum); @@ -1181,11 +1181,11 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, * Clear stale usb2 resume signalling variables in case port changed * state during resume signalling. For example on error */ - if ((port->resume_done || + if ((port->resume_timestamp || test_bit(wIndex, &bus_state->resuming_ports)) && (raw_port_status & PORT_PLS_MASK) != XDEV_U3 && (raw_port_status & PORT_PLS_MASK) != XDEV_RESUME) { - port->resume_done = 0; + port->resume_timestamp = 0; clear_bit(wIndex, &bus_state->resuming_ports); usb_hcd_end_port_resume(&hcd->self, wIndex); } @@ -1693,8 +1693,8 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) if ((temp & mask) != 0 || (bus_state->port_c_suspend & 1 << i) || - (ports[i]->resume_done && time_after_eq( - jiffies, ports[i]->resume_done))) { + (ports[i]->resume_timestamp && time_after_eq( + jiffies, ports[i]->resume_timestamp))) { buf[(i + 1) / 8] |= 1 << (i + 1) % 8; status = 1; } diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 611580d4adad..eb788c60c1c0 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1924,7 +1924,7 @@ static void handle_port_status(struct xhci_hcd *xhci, goto cleanup; } else if (!test_bit(hcd_portnum, &bus_state->resuming_ports)) { xhci_dbg(xhci, "resume HS port %d\n", port_id); - port->resume_done = jiffies + + port->resume_timestamp = jiffies + msecs_to_jiffies(USB_RESUME_TIMEOUT); set_bit(hcd_portnum, &bus_state->resuming_ports); /* Do the rest in GetPortStatus after resume time delay. @@ -1933,7 +1933,7 @@ static void handle_port_status(struct xhci_hcd *xhci, */ set_bit(HCD_FLAG_POLL_RH, &hcd->flags); mod_timer(&hcd->rh_timer, - port->resume_done); + port->resume_timestamp); usb_hcd_start_port_resume(&hcd->self, hcd_portnum); bogus_port_status = true; } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 578e219292fd..786002bb35db 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1740,7 +1740,7 @@ struct xhci_port { struct xhci_hub *rhub; struct xhci_port_cap *port_cap; unsigned int lpm_incapable:1; - unsigned long resume_done; + unsigned long resume_timestamp; bool rexit_active; struct completion rexit_done; struct completion u3exit_done; -- cgit v1.2.3 From 0e6275452ce26d7ff274a5c1b15ed581a26f7986 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Thu, 2 Feb 2023 17:05:04 +0200 Subject: xhci: clear usb2 resume related variables in one place. Initially resume related USB2 variables were cleared once port successfully resumed to U0. Later code was added to clean up stale resume variables in case of port failed to resume to U0. Clear the variables in one place after port is no longer resuming or in suspended U3 state. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-11-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index a2053aa9b4a9..541ccc45ea51 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -1090,7 +1090,6 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, struct xhci_bus_state *bus_state; u32 link_state; u32 portnum; - int ret; bus_state = &port->rhub->bus_state; link_state = portsc & PORT_PLS_MASK; @@ -1106,23 +1105,30 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, if (link_state == XDEV_U2) *status |= USB_PORT_STAT_L1; if (link_state == XDEV_U0) { - if (port->resume_timestamp) - usb_hcd_end_port_resume(&port->rhub->hcd->self, - portnum); - port->resume_timestamp = 0; - clear_bit(portnum, &bus_state->resuming_ports); if (bus_state->suspended_ports & (1 << portnum)) { bus_state->suspended_ports &= ~(1 << portnum); bus_state->port_c_suspend |= 1 << portnum; } } if (link_state == XDEV_RESUME) { - ret = xhci_handle_usb2_port_link_resume(port, status, - portsc, flags); - if (ret) - return; + xhci_handle_usb2_port_link_resume(port, status, portsc, + flags); } } + + /* + * Clear usb2 resume signalling variables if port is no longer suspended + * or resuming. Port either resumed to U0/U1/U2, disconnected, or in a + * error state. Resume related variables should be cleared in all those cases. + */ + if ((link_state != XDEV_U3 && + link_state != XDEV_RESUME) && + (port->resume_timestamp || + test_bit(portnum, &bus_state->resuming_ports))) { + port->resume_timestamp = 0; + clear_bit(portnum, &bus_state->resuming_ports); + usb_hcd_end_port_resume(&port->rhub->hcd->self, portnum); + } } /* @@ -1177,18 +1183,6 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, else xhci_get_usb2_port_status(port, &status, raw_port_status, flags); - /* - * Clear stale usb2 resume signalling variables in case port changed - * state during resume signalling. For example on error - */ - if ((port->resume_timestamp || - test_bit(wIndex, &bus_state->resuming_ports)) && - (raw_port_status & PORT_PLS_MASK) != XDEV_U3 && - (raw_port_status & PORT_PLS_MASK) != XDEV_RESUME) { - port->resume_timestamp = 0; - clear_bit(wIndex, &bus_state->resuming_ports); - usb_hcd_end_port_resume(&hcd->self, wIndex); - } if (bus_state->port_c_suspend & (1 << wIndex)) status |= USB_PORT_STAT_C_SUSPEND << 16; -- cgit v1.2.3 From b0425784b942fffbbdb804896197f1dbccda37c5 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Thu, 2 Feb 2023 17:05:05 +0200 Subject: xhci: decouple usb2 port resume and get_port_status request handling The get port status hub request code in xhci-hub.c will complete usb2 port resume signalling if signalling has been going on for long enough. The code that completes the resume signalling, and the code that returns the port status have gotten too intertwined, so separate them a bit. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-12-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 47 ++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 22 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 541ccc45ea51..0054d02239e2 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -924,7 +924,7 @@ static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, } static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, - u32 *status, u32 portsc, + u32 portsc, unsigned long *flags) { struct xhci_bus_state *bus_state; @@ -939,7 +939,6 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, wIndex = port->hcd_portnum; if ((portsc & PORT_RESET) || !(portsc & PORT_PE)) { - *status = 0xffffffff; return -EINVAL; } /* did port event handler already start resume timing? */ @@ -973,6 +972,8 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, port->resume_timestamp = 0; clear_bit(wIndex, &bus_state->resuming_ports); + + reinit_completion(&port->rexit_done); port->rexit_active = true; xhci_test_and_clear_bit(xhci, port, PORT_PLC); @@ -989,7 +990,6 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, wIndex + 1); if (!slot_id) { xhci_dbg(xhci, "slot_id is zero\n"); - *status = 0xffffffff; return -ENODEV; } xhci_ring_device(xhci, slot_id); @@ -998,22 +998,19 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, xhci_warn(xhci, "Port resume timed out, port %d-%d: 0x%x\n", hcd->self.busnum, wIndex + 1, port_status); - *status |= USB_PORT_STAT_SUSPEND; - port->rexit_active = false; + /* + * keep rexit_active set if U0 transition failed so we + * know to report PORT_STAT_SUSPEND status back to + * usbcore. It will be cleared later once the port is + * out of RESUME/U3 state + */ } usb_hcd_end_port_resume(&hcd->self, wIndex); bus_state->port_c_suspend |= 1 << wIndex; bus_state->suspended_ports &= ~(1 << wIndex); - } else { - /* - * The resume has been signaling for less than - * USB_RESUME_TIME. Report the port status as SUSPEND, - * let the usbcore check port status again and clear - * resume signaling later. - */ - *status |= USB_PORT_STAT_SUSPEND; } + return 0; } @@ -1090,6 +1087,7 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, struct xhci_bus_state *bus_state; u32 link_state; u32 portnum; + int err; bus_state = &port->rhub->bus_state; link_state = portsc & PORT_PLS_MASK; @@ -1111,8 +1109,12 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, } } if (link_state == XDEV_RESUME) { - xhci_handle_usb2_port_link_resume(port, status, portsc, - flags); + err = xhci_handle_usb2_port_link_resume(port, portsc, + flags); + if (err < 0) + *status = 0xffffffff; + else if (port->resume_timestamp || port->rexit_active) + *status |= USB_PORT_STAT_SUSPEND; } } @@ -1121,13 +1123,14 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, * or resuming. Port either resumed to U0/U1/U2, disconnected, or in a * error state. Resume related variables should be cleared in all those cases. */ - if ((link_state != XDEV_U3 && - link_state != XDEV_RESUME) && - (port->resume_timestamp || - test_bit(portnum, &bus_state->resuming_ports))) { - port->resume_timestamp = 0; - clear_bit(portnum, &bus_state->resuming_ports); - usb_hcd_end_port_resume(&port->rhub->hcd->self, portnum); + if (link_state != XDEV_U3 && link_state != XDEV_RESUME) { + if (port->resume_timestamp || + test_bit(portnum, &bus_state->resuming_ports)) { + port->resume_timestamp = 0; + clear_bit(portnum, &bus_state->resuming_ports); + usb_hcd_end_port_resume(&port->rhub->hcd->self, portnum); + } + port->rexit_active = 0; } } -- cgit v1.2.3 From 4f6dfc2136fb2e8dc3f571a5caff6b6e88281fc0 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 13 Jan 2023 07:23:19 +0100 Subject: usb: remove the dead USB_OHCI_SH option USB_OHCI_SH is a dummy option that never builds any code, remove it. Signed-off-by: Christoph Hellwig Link: https://lore.kernel.org/r/20230113062339.1909087-3-hch@lst.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 0a54190bb097..1b6b83e79607 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -557,17 +557,6 @@ config USB_OHCI_HCD_SSB If unsure, say N. -config USB_OHCI_SH - bool "OHCI support for SuperH USB controller (DEPRECATED)" - depends on SUPERH || COMPILE_TEST - select USB_OHCI_HCD_PLATFORM - help - This option is deprecated now and the driver was removed, use - USB_OHCI_HCD_PLATFORM instead. - - Enables support for the on-chip OHCI controller on the SuperH. - If you use the PCI OHCI controller, this option is not necessary. - config USB_OHCI_EXYNOS tristate "OHCI support for Samsung S5P/Exynos SoC Series" depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST -- cgit v1.2.3 From 0a3f82c79c86278e7f144564b1cb6cc5c3657144 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 2 Feb 2023 16:32:25 +0100 Subject: USB: uhci: fix memory leak with using debugfs_lookup() When calling debugfs_lookup() the result must have dput() called on it, otherwise the memory will leak over time. To make things simpler, just call debugfs_lookup_and_remove() instead which handles all of the logic at once. Cc: Alan Stern Link: https://lore.kernel.org/r/20230202153235.2412790-3-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hcd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index c22b51af83fc..7cdc2fa7c28f 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -536,8 +536,8 @@ static void release_uhci(struct uhci_hcd *uhci) uhci->is_initialized = 0; spin_unlock_irq(&uhci->lock); - debugfs_remove(debugfs_lookup(uhci_to_hcd(uhci)->self.bus_name, - uhci_debugfs_root)); + debugfs_lookup_and_remove(uhci_to_hcd(uhci)->self.bus_name, + uhci_debugfs_root); for (i = 0; i < UHCI_NUM_SKELQH; i++) uhci_free_qh(uhci, uhci->skelqh[i]); @@ -700,7 +700,7 @@ err_alloc_frame_cpu: uhci->frame, uhci->frame_dma_handle); err_alloc_frame: - debugfs_remove(debugfs_lookup(hcd->self.bus_name, uhci_debugfs_root)); + debugfs_lookup_and_remove(hcd->self.bus_name, uhci_debugfs_root); return retval; } -- cgit v1.2.3 From e1523c4dbc54e164638ff8729d511cf91e27be04 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 2 Feb 2023 16:32:26 +0100 Subject: USB: sl811: fix memory leak with using debugfs_lookup() When calling debugfs_lookup() the result must have dput() called on it, otherwise the memory will leak over time. To make things simpler, just call debugfs_lookup_and_remove() instead which handles all of the logic at once. Cc: Vincent Mailhol Link: https://lore.kernel.org/r/20230202153235.2412790-4-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/sl811-hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index d206bd95c7bb..b8b90eec9107 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1501,7 +1501,7 @@ static void create_debug_file(struct sl811 *sl811) static void remove_debug_file(struct sl811 *sl811) { - debugfs_remove(debugfs_lookup("sl811h", usb_debug_root)); + debugfs_lookup_and_remove("sl811h", usb_debug_root); } /*-------------------------------------------------------------------------*/ -- cgit v1.2.3 From a95f62d5813facbec20ec087472eb313ee5fa8af Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 2 Feb 2023 16:32:28 +0100 Subject: USB: isp116x: fix memory leak with using debugfs_lookup() When calling debugfs_lookup() the result must have dput() called on it, otherwise the memory will leak over time. To make things simpler, just call debugfs_lookup_and_remove() instead which handles all of the logic at once. Cc: Olav Kongas Link: https://lore.kernel.org/r/20230202153235.2412790-6-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp116x-hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index 4f564d71bb0b..49ae01487af4 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -1205,7 +1205,7 @@ static void create_debug_file(struct isp116x *isp116x) static void remove_debug_file(struct isp116x *isp116x) { - debugfs_remove(debugfs_lookup(hcd_name, usb_debug_root)); + debugfs_lookup_and_remove(hcd_name, usb_debug_root); } #else -- cgit v1.2.3 From c26e682afc14caa87d44beed271eec8991e93c65 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 2 Feb 2023 16:32:29 +0100 Subject: USB: isp1362: fix memory leak with using debugfs_lookup() When calling debugfs_lookup() the result must have dput() called on it, otherwise the memory will leak over time. To make things simpler, just call debugfs_lookup_and_remove() instead which handles all of the logic at once. Cc: Vincent Mailhol Link: https://lore.kernel.org/r/20230202153235.2412790-7-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp1362-hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index 0e14d1d07709..b0da143ef4be 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -2170,7 +2170,7 @@ static void create_debug_file(struct isp1362_hcd *isp1362_hcd) static void remove_debug_file(struct isp1362_hcd *isp1362_hcd) { - debugfs_remove(debugfs_lookup("isp1362", usb_debug_root)); + debugfs_lookup_and_remove("isp1362", usb_debug_root); } /*-------------------------------------------------------------------------*/ -- cgit v1.2.3 From 0fbd2cda92cdb00f72080665554a586f88bca821 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Sat, 4 Feb 2023 10:36:52 -0800 Subject: usb: host: xhci: mvebu: Iterate over array indexes instead of using pointer math Walking the dram->cs array was seen as accesses beyond the first array item by the compiler. Instead, use the array index directly. This allows for run-time bounds checking under CONFIG_UBSAN_BOUNDS as well. Seen with GCC 13 with -fstrict-flex-arrays: In function 'xhci_mvebu_mbus_config', inlined from 'xhci_mvebu_mbus_init_quirk' at ../drivers/usb/host/xhci-mvebu.c:66:2: ../drivers/usb/host/xhci-mvebu.c:37:28: warning: array subscript 0 is outside array bounds of 'const struct mbus_dram_window[0]' [-Warray-bounds=] 37 | writel(((cs->size - 1) & 0xffff0000) | (cs->mbus_attr << 8) | | ~~^~~~~~ Cc: Mathias Nyman Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20230204183651.never.663-kees@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mvebu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/xhci-mvebu.c b/drivers/usb/host/xhci-mvebu.c index 60651a50770f..87f1597a0e5a 100644 --- a/drivers/usb/host/xhci-mvebu.c +++ b/drivers/usb/host/xhci-mvebu.c @@ -32,7 +32,7 @@ static void xhci_mvebu_mbus_config(void __iomem *base, /* Program each DRAM CS in a seperate window */ for (win = 0; win < dram->num_cs; win++) { - const struct mbus_dram_window *cs = dram->cs + win; + const struct mbus_dram_window *cs = &dram->cs[win]; writel(((cs->size - 1) & 0xffff0000) | (cs->mbus_attr << 8) | (dram->mbus_dram_target_id << 4) | 1, -- cgit v1.2.3 From a7efe3fc7cbe27c6eb2c2a3ab612194f8f800f4c Mon Sep 17 00:00:00 2001 From: Mark Tomlinson Date: Tue, 7 Feb 2023 16:33:37 +1300 Subject: usb: max-3421: Fix setting of I/O pins To update the I/O pins, the registers are read/modified/written. The read operation incorrectly always read the first register. Although wrong, there wasn't any impact as all the output pins are always written, and the inputs are read only anyway. Fixes: 2d53139f3162 ("Add support for using a MAX3421E chip as a host driver.") Signed-off-by: Mark Tomlinson Link: https://lore.kernel.org/r/20230207033337.18112-1-mark.tomlinson@alliedtelesis.co.nz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/max3421-hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c index 9a87056fc738..28d1524ee2fa 100644 --- a/drivers/usb/host/max3421-hcd.c +++ b/drivers/usb/host/max3421-hcd.c @@ -1427,7 +1427,7 @@ max3421_spi_thread(void *dev_id) * use spi_wr_buf(). */ for (i = 0; i < ARRAY_SIZE(max3421_hcd->iopins); ++i) { - u8 val = spi_rd8(hcd, MAX3421_REG_IOPINS1); + u8 val = spi_rd8(hcd, MAX3421_REG_IOPINS1 + i); val = ((val & 0xf0) | (max3421_hcd->iopins[i] & 0x0f)); -- cgit v1.2.3 From e2ffae3ed92a9f768902c1cf82642c3a09cd0345 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Tue, 7 Feb 2023 12:05:31 +0100 Subject: usb: host: fsl-mph-dr-of: reuse device_set_of_node_from_dev This sets both of_node fields and takes a of_node reference as well. Fixes: bb160ee61c04 ("drivers/usb/host/ehci-fsl: Fix interrupt setup in host mode.") Signed-off-by: Alexander Stein Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20230207110531.1060252-4-alexander.stein@ew.tq-group.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/fsl-mph-dr-of.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c index e5df17522892..46c6a152b865 100644 --- a/drivers/usb/host/fsl-mph-dr-of.c +++ b/drivers/usb/host/fsl-mph-dr-of.c @@ -112,8 +112,7 @@ static struct platform_device *fsl_usb2_device_register( goto error; } - pdev->dev.of_node = ofdev->dev.of_node; - pdev->dev.of_node_reused = true; + device_set_of_node_from_dev(&pdev->dev, &ofdev->dev); retval = platform_device_add(pdev); if (retval) -- cgit v1.2.3 From 77191db5ba7bd321fbbf4315675ee774a2b5a362 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 9 Feb 2023 16:43:45 +0300 Subject: xhci: host: potential NULL dereference in xhci_generic_plat_probe() It's possible to exit the loop with "sysdev" set to NULL. In that case we should use "&pdev->dev". Fixes: ec5499d338ec ("xhci: split out rcar/rz support from xhci-plat.c") Signed-off-by: Dan Carpenter Acked-by: Arnd Bergmann Link: https://lore.kernel.org/r/Y+T4kTcJwRwxNHJq@kili Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-plat.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index cd17ccab6e00..b9f9625467d6 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -368,6 +368,9 @@ static int xhci_generic_plat_probe(struct platform_device *pdev) #endif } + if (!sysdev) + sysdev = &pdev->dev; + if (WARN_ON(!sysdev->dma_mask)) { /* Platform did not initialize dma_mask */ ret = dma_coerce_mask_and_coherent(sysdev, DMA_BIT_MASK(64)); -- cgit v1.2.3