| From b7863709a25d4ec14a2a22dc138c25b2cf349bd9 Mon Sep 17 00:00:00 2001 |
| From: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> |
| Date: Fri, 17 Jul 2020 20:44:56 +0900 |
| Subject: [PATCH] phy: renesas: rcar-gen3-usb2: move irq registration to init |
| |
| commit 08b0ad375ca66181faee725b1b358bcae8d592ee upstream. |
| |
| If CONFIG_DEBUG_SHIRQ was enabled, r8a77951-salvator-xs could boot |
| correctly. If we appended "earlycon keep_bootcon" to the kernel |
| command like, we could get kernel log like below. |
| |
| SError Interrupt on CPU0, code 0xbf000002 -- SError |
| CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.8.0-rc3-salvator-x-00505-g6c843129e6faaf01 #785 |
| Hardware name: Renesas Salvator-X 2nd version board based on r8a77951 (DT) |
| pstate: 60400085 (nZCv daIf +PAN -UAO BTYPE=--) |
| pc : rcar_gen3_phy_usb2_irq+0x14/0x54 |
| lr : free_irq+0xf4/0x27c |
| |
| This means free_irq() calls the interrupt handler while PM runtime |
| is not getting if DEBUG_SHIRQ is enabled and rcar_gen3_phy_usb2_probe() |
| failed. To fix the issue, move the irq registration place to |
| rcar_gen3_phy_usb2_init() which is ready to handle the interrupts. |
| |
| Note that after the commit 549b6b55b005 ("phy: renesas: rcar-gen3-usb2: |
| enable/disable independent irqs") which is merged into v5.2, since this |
| driver creates multiple phy instances, needs to check whether one of |
| phy instances is initialized. However, if we backport this patch to v5.1 |
| or less, we don't need to check it because such kernel have single |
| phy instance. |
| |
| Reported-by: Wolfram Sang <wsa+renesas@sang-engineering.com> |
| Reported-by: Geert Uytterhoeven <geert+renesas@glider.be> |
| Fixes: 9f391c574efc ("phy: rcar-gen3-usb2: add runtime ID/VBUS pin detection") |
| Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> |
| Link: https://lore.kernel.org/r/1594986297-12434-2-git-send-email-yoshihiro.shimoda.uh@renesas.com |
| Signed-off-by: Vinod Koul <vkoul@kernel.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c |
| index bfb22f868857..5087b7c44d55 100644 |
| --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c |
| +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c |
| @@ -111,6 +111,7 @@ struct rcar_gen3_chan { |
| struct work_struct work; |
| struct mutex lock; /* protects rphys[...].powered */ |
| enum usb_dr_mode dr_mode; |
| + int irq; |
| bool extcon_host; |
| bool is_otg_channel; |
| bool uses_otg_pins; |
| @@ -389,12 +390,38 @@ static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch) |
| rcar_gen3_device_recognition(ch); |
| } |
| |
| +static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch) |
| +{ |
| + struct rcar_gen3_chan *ch = _ch; |
| + void __iomem *usb2_base = ch->base; |
| + u32 status = readl(usb2_base + USB2_OBINTSTA); |
| + irqreturn_t ret = IRQ_NONE; |
| + |
| + if (status & USB2_OBINT_BITS) { |
| + dev_vdbg(ch->dev, "%s: %08x\n", __func__, status); |
| + writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA); |
| + rcar_gen3_device_recognition(ch); |
| + ret = IRQ_HANDLED; |
| + } |
| + |
| + return ret; |
| +} |
| + |
| static int rcar_gen3_phy_usb2_init(struct phy *p) |
| { |
| struct rcar_gen3_phy *rphy = phy_get_drvdata(p); |
| struct rcar_gen3_chan *channel = rphy->ch; |
| void __iomem *usb2_base = channel->base; |
| u32 val; |
| + int ret; |
| + |
| + if (!rcar_gen3_is_any_rphy_initialized(channel) && channel->irq >= 0) { |
| + INIT_WORK(&channel->work, rcar_gen3_phy_usb2_work); |
| + ret = request_irq(channel->irq, rcar_gen3_phy_usb2_irq, |
| + IRQF_SHARED, dev_name(channel->dev), channel); |
| + if (ret < 0) |
| + dev_err(channel->dev, "No irq handler (%d)\n", channel->irq); |
| + } |
| |
| /* Initialize USB2 part */ |
| val = readl(usb2_base + USB2_INT_ENABLE); |
| @@ -433,6 +460,9 @@ static int rcar_gen3_phy_usb2_exit(struct phy *p) |
| val &= ~USB2_INT_ENABLE_UCOM_INTEN; |
| writel(val, usb2_base + USB2_INT_ENABLE); |
| |
| + if (channel->irq >= 0 && !rcar_gen3_is_any_rphy_initialized(channel)) |
| + free_irq(channel->irq, channel); |
| + |
| return 0; |
| } |
| |
| @@ -503,23 +533,6 @@ static const struct phy_ops rz_g1c_phy_usb2_ops = { |
| .owner = THIS_MODULE, |
| }; |
| |
| -static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch) |
| -{ |
| - struct rcar_gen3_chan *ch = _ch; |
| - void __iomem *usb2_base = ch->base; |
| - u32 status = readl(usb2_base + USB2_OBINTSTA); |
| - irqreturn_t ret = IRQ_NONE; |
| - |
| - if (status & USB2_OBINT_BITS) { |
| - dev_vdbg(ch->dev, "%s: %08x\n", __func__, status); |
| - writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA); |
| - rcar_gen3_device_recognition(ch); |
| - ret = IRQ_HANDLED; |
| - } |
| - |
| - return ret; |
| -} |
| - |
| static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = { |
| { |
| .compatible = "renesas,usb2-phy-r8a77470", |
| @@ -598,7 +611,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) |
| struct phy_provider *provider; |
| struct resource *res; |
| const struct phy_ops *phy_usb2_ops; |
| - int irq, ret = 0, i; |
| + int ret = 0, i; |
| |
| if (!dev->of_node) { |
| dev_err(dev, "This driver needs device tree\n"); |
| @@ -614,16 +627,8 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) |
| if (IS_ERR(channel->base)) |
| return PTR_ERR(channel->base); |
| |
| - /* call request_irq for OTG */ |
| - irq = platform_get_irq_optional(pdev, 0); |
| - if (irq >= 0) { |
| - INIT_WORK(&channel->work, rcar_gen3_phy_usb2_work); |
| - irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq, |
| - IRQF_SHARED, dev_name(dev), channel); |
| - if (irq < 0) |
| - dev_err(dev, "No irq handler (%d)\n", irq); |
| - } |
| - |
| + /* get irq number here and request_irq for OTG in phy_init */ |
| + channel->irq = platform_get_irq_optional(pdev, 0); |
| channel->dr_mode = rcar_gen3_get_dr_mode(dev->of_node); |
| if (channel->dr_mode != USB_DR_MODE_UNKNOWN) { |
| int ret; |
| -- |
| 2.27.0 |
| |