Hello Yoshihiro-san, Thank you for your partch! > From: devicetree-owner@xxxxxxxxxxxxxxx <devicetree-owner@xxxxxxxxxxxxxxx> On Behalf Of Yoshihiro Shimoda > Sent: 01 April 2019 13:01 > Subject: [PATCH 3/3] phy: renesas: rcar-gen3-usb2: enable/disable independent irqs > > Since the previous code enabled/disabled the irqs both OHCI and EHCI, > it is possible to cause unexpected interruptions. To avoid this, > this patch creates multiple phy instances from phandle and > enables/disables independent irqs by the instances. > > Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@xxxxxxxxxxx> Reviewed-by: Fabrizio Castro <fabrizio.castro@xxxxxxxxxxxxxx> Tested-by: Fabrizio Castro <fabrizio.castro@xxxxxxxxxxxxxx> > --- > drivers/phy/renesas/phy-rcar-gen3-usb2.c | 181 ++++++++++++++++++++++++++----- > 1 file changed, 156 insertions(+), 25 deletions(-) > > diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c > index 4bdb2ed..bbe0fe5 100644 > --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c > +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c > @@ -37,11 +37,8 @@ > > /* INT_ENABLE */ > #define USB2_INT_ENABLE_UCOM_INTEN BIT(3) > -#define USB2_INT_ENABLE_USBH_INTB_EN BIT(2) > -#define USB2_INT_ENABLE_USBH_INTA_EN BIT(1) > -#define USB2_INT_ENABLE_INIT (USB2_INT_ENABLE_UCOM_INTEN | \ > - USB2_INT_ENABLE_USBH_INTB_EN | \ > - USB2_INT_ENABLE_USBH_INTA_EN) > +#define USB2_INT_ENABLE_USBH_INTB_EN BIT(2) /* For EHCI */ > +#define USB2_INT_ENABLE_USBH_INTA_EN BIT(1) /* For OHCI */ > > /* USBCTR */ > #define USB2_USBCTR_DIRPD BIT(2) > @@ -78,11 +75,33 @@ > #define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */ > #define USB2_ADPCTRL_DRVVBUS BIT(4) > > +#define NUM_OF_PHYS 4 > +#define PHY_INDEX_BOTH_HC 0 > +#define PHY_INDEX_OHCI 1 > +#define PHY_INDEX_EHCI 2 > +#define PHY_INDEX_HSUSB 3 > + > +static const u32 rcar_gen3_int_enable[NUM_OF_PHYS] = { > + USB2_INT_ENABLE_USBH_INTB_EN | USB2_INT_ENABLE_USBH_INTA_EN, > + USB2_INT_ENABLE_USBH_INTA_EN, > + USB2_INT_ENABLE_USBH_INTB_EN, > + 0 > +}; > + > +struct rcar_gen3_phy { > + struct phy *phy; > + struct rcar_gen3_chan *ch; > + u32 int_enable_bits; > + bool initialized; > + bool otg_initialized; > + bool powered; > +}; > + > struct rcar_gen3_chan { > void __iomem *base; > struct device *dev; /* platform_device's device */ > struct extcon_dev *extcon; > - struct phy *phy; > + struct rcar_gen3_phy rphys[NUM_OF_PHYS]; > struct regulator *vbus; > struct work_struct work; > enum usb_dr_mode dr_mode; > @@ -250,6 +269,42 @@ static enum phy_mode rcar_gen3_get_phy_mode(struct rcar_gen3_chan *ch) > return PHY_MODE_USB_DEVICE; > } > > +static bool rcar_gen3_is_any_rphy_initialized(struct rcar_gen3_chan *ch) > +{ > + int i; > + > + for (i = 0; i < NUM_OF_PHYS; i++) { > + if (ch->rphys[i].initialized) > + return true; > + } > + > + return false; > +} > + > +static bool rcar_gen3_needs_init_otg(struct rcar_gen3_chan *ch) > +{ > + int i; > + > + for (i = 0; i < NUM_OF_PHYS; i++) { > + if (ch->rphys[i].otg_initialized) > + return false; > + } > + > + return true; > +} > + > +static bool rcar_gen3_are_all_rphys_power_off(struct rcar_gen3_chan *ch) > +{ > + int i; > + > + for (i = 0; i < NUM_OF_PHYS; i++) { > + if (ch->rphys[i].powered) > + return false; > + } > + > + return true; > +} > + > static ssize_t role_store(struct device *dev, struct device_attribute *attr, > const char *buf, size_t count) > { > @@ -257,7 +312,7 @@ static ssize_t role_store(struct device *dev, struct device_attribute *attr, > bool is_b_device; > enum phy_mode cur_mode, new_mode; > > - if (!ch->is_otg_channel || !ch->phy->init_count) > + if (!ch->is_otg_channel || !rcar_gen3_is_any_rphy_initialized(ch)) > return -EIO; > > if (!strncmp(buf, "host", strlen("host"))) > @@ -295,7 +350,7 @@ static ssize_t role_show(struct device *dev, struct device_attribute *attr, > { > struct rcar_gen3_chan *ch = dev_get_drvdata(dev); > > - if (!ch->is_otg_channel || !ch->phy->init_count) > + if (!ch->is_otg_channel || !rcar_gen3_is_any_rphy_initialized(ch)) > return -EIO; > > return sprintf(buf, "%s\n", rcar_gen3_is_host(ch) ? "host" : > @@ -329,37 +384,62 @@ static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch) > > static int rcar_gen3_phy_usb2_init(struct phy *p) > { > - struct rcar_gen3_chan *channel = phy_get_drvdata(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; > > /* Initialize USB2 part */ > - writel(USB2_INT_ENABLE_INIT, usb2_base + USB2_INT_ENABLE); > + val = readl(usb2_base + USB2_INT_ENABLE); > + val |= USB2_INT_ENABLE_UCOM_INTEN | rphy->int_enable_bits; > + writel(val, usb2_base + USB2_INT_ENABLE); > writel(USB2_SPD_RSM_TIMSET_INIT, usb2_base + USB2_SPD_RSM_TIMSET); > writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET); > > /* Initialize otg part */ > - if (channel->is_otg_channel) > - rcar_gen3_init_otg(channel); > + if (channel->is_otg_channel) { > + if (rcar_gen3_needs_init_otg(channel)) > + rcar_gen3_init_otg(channel); > + rphy->otg_initialized = true; > + } > + > + rphy->initialized = true; > > return 0; > } > > static int rcar_gen3_phy_usb2_exit(struct phy *p) > { > - struct rcar_gen3_chan *channel = phy_get_drvdata(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; > > - writel(0, channel->base + USB2_INT_ENABLE); > + rphy->initialized = false; > + > + if (channel->is_otg_channel) > + rphy->otg_initialized = false; > + > + val = readl(usb2_base + USB2_INT_ENABLE); > + val &= ~rphy->int_enable_bits; > + if (!rcar_gen3_is_any_rphy_initialized(channel)) > + val &= ~USB2_INT_ENABLE_UCOM_INTEN; > + writel(val, usb2_base + USB2_INT_ENABLE); > > return 0; > } > > static int rcar_gen3_phy_usb2_power_on(struct phy *p) > { > - struct rcar_gen3_chan *channel = phy_get_drvdata(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_are_all_rphys_power_off(channel)) > + return 0; > + > if (channel->vbus) { > ret = regulator_enable(channel->vbus); > if (ret) > @@ -372,14 +452,22 @@ static int rcar_gen3_phy_usb2_power_on(struct phy *p) > val &= ~USB2_USBCTR_PLL_RST; > writel(val, usb2_base + USB2_USBCTR); > > + rphy->powered = true; > + > return 0; > } > > static int rcar_gen3_phy_usb2_power_off(struct phy *p) > { > - struct rcar_gen3_chan *channel = phy_get_drvdata(p); > + struct rcar_gen3_phy *rphy = phy_get_drvdata(p); > + struct rcar_gen3_chan *channel = rphy->ch; > int ret = 0; > > + rphy->powered = false; > + > + if (!rcar_gen3_are_all_rphys_power_off(channel)) > + return 0; > + > if (channel->vbus) > ret = regulator_disable(channel->vbus); > > @@ -426,13 +514,51 @@ static const unsigned int rcar_gen3_phy_cable[] = { > EXTCON_NONE, > }; > > +static struct phy *rcar_gen3_phy_usb2_xlate(struct device *dev, > + struct of_phandle_args *args) > +{ > + struct rcar_gen3_chan *ch = dev_get_drvdata(dev); > + > + if (args->args_count == 0) /* For old version dts */ > + return ch->rphys[PHY_INDEX_BOTH_HC].phy; > + else if (args->args_count > 1) /* Prevent invalid args count */ > + return ERR_PTR(-ENODEV); > + > + if (args->args[0] >= NUM_OF_PHYS) > + return ERR_PTR(-ENODEV); > + > + return ch->rphys[args->args[0]].phy; > +} > + > +static enum usb_dr_mode rcar_gen3_get_dr_mode(struct device_node *np) > +{ > + enum usb_dr_mode candidate = USB_DR_MODE_UNKNOWN, tmp; > + int i; > + > + /* > + * If one of device nodes has other dr_mode except UNKNOWN, > + * this function returns UNKNOWN. > + */ > + for (i = 0; i < NUM_OF_PHYS; i++) { > + tmp = of_usb_get_dr_mode_by_phy(np, i); We are calling of_usb_get_dr_mode_by_phy with i == 0, but we don't document it in the dt-bindings document? > + if (tmp != USB_DR_MODE_UNKNOWN) { > + if (candidate == USB_DR_MODE_UNKNOWN) > + candidate = tmp; > + else if (candidate != tmp) > + return USB_DR_MODE_UNKNOWN; > + } > + } > + > + return candidate; > +} > + > static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) > { > struct device *dev = &pdev->dev; > struct rcar_gen3_chan *channel; > struct phy_provider *provider; > struct resource *res; > - int irq, ret = 0; > + int irq, ret = 0, i; > > if (!dev->of_node) { > dev_err(dev, "This driver needs device tree\n"); > @@ -458,7 +584,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) > dev_err(dev, "No irq handler (%d)\n", irq); > } > > - channel->dr_mode = of_usb_get_dr_mode_by_phy(dev->of_node, 0); > + channel->dr_mode = rcar_gen3_get_dr_mode(dev->of_node); > if (channel->dr_mode != USB_DR_MODE_UNKNOWN) { > int ret; > > @@ -482,11 +608,17 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) > * And then, phy-core will manage runtime pm for this device. > */ > pm_runtime_enable(dev); > - channel->phy = devm_phy_create(dev, NULL, &rcar_gen3_phy_usb2_ops); > - if (IS_ERR(channel->phy)) { > - dev_err(dev, "Failed to create USB2 PHY\n"); > - ret = PTR_ERR(channel->phy); > - goto error; > + for (i = 0; i < NUM_OF_PHYS; i++) { > + channel->rphys[i].phy = devm_phy_create(dev, NULL, > + &rcar_gen3_phy_usb2_ops); > + if (IS_ERR(channel->rphys[i].phy)) { > + dev_err(dev, "Failed to create USB2 PHY\n"); > + ret = PTR_ERR(channel->rphys[i].phy); > + goto error; > + } > + channel->rphys[i].ch = channel; > + channel->rphys[i].int_enable_bits = rcar_gen3_int_enable[i]; > + phy_set_drvdata(channel->rphys[i].phy, &channel->rphys[i]); > } > > channel->vbus = devm_regulator_get_optional(dev, "vbus"); > @@ -499,10 +631,9 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) > } > > platform_set_drvdata(pdev, channel); > - phy_set_drvdata(channel->phy, channel); > channel->dev = dev; > > - provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); > + provider = devm_of_phy_provider_register(dev, rcar_gen3_phy_usb2_xlate); > if (IS_ERR(provider)) { > dev_err(dev, "Failed to register PHY provider\n"); > ret = PTR_ERR(provider); > -- > 2.7.4