RE: [PATCH 3/3] phy: renesas: rcar-gen3-usb2: enable/disable independent irqs

From: Fabrizio Castro
Date: Wed Apr 03 2019 - 06:49:33 EST


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