RE: [PATCH v2] clk: renesas: rcar-usb2-clock-sel: Add R-Car USB 2.0 clock selector PHY
From: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Date: 2017-07-05 11:57:17
Also in:
linux-clk, linux-renesas-soc
Hi Geert-san,
From: Geert Uytterhoeven Sent: Wednesday, July 5, 2017 7:09 PM Hi Simoda-san, On Wed, Jun 28, 2017 at 8:28 AM, Yoshihiro Shimoda [off-list ref] wrote:quoted
R-Car USB 2.0 controller can change the clock source from an oscillator to an external clock via a register. So, this patch adds support the clock source selector as a clock driver. Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>Thanks for your patch!
Thank you very much for the comments!
quoted
--- /dev/null +++ b/drivers/clk/renesas/rcar-usb2-clock-sel.cquoted
+static void usb2_clock_sel_enable_extal_only(struct usb2_clock_sel_priv *priv) +{ + u16 val = readw(priv->base + USB20_CLKSET0); + + pr_debug("%s: enter %d %d %x\n", __func__, + priv->extal, priv->xtal, val); + + if (priv->extal && !priv->xtal && val != CLKSET0_EXTAL_ONLY) + writew(CLKSET0_EXTAL_ONLY, priv->base + USB20_CLKSET0); +} + +static void usb2_clock_sel_disable_extal_only(struct usb2_clock_sel_priv *priv) +{ + if (priv->extal && !priv->xtal) + writew(CLKSET0_PRIVATE, priv->base + USB20_CLKSET0); +} + +static int usb2_clock_sel_enable(struct clk_hw *hw) +{ + usb2_clock_sel_enable_extal_only(to_priv(hw)); + + return 0; +} + +static void usb2_clock_sel_disable(struct clk_hw *hw) +{ + usb2_clock_sel_disable_extal_only(to_priv(hw)); +} + +/* + * This module seems a mux, but this driver assumes a gate because + * ehci/ohci platform drivers don't support clk_set_parent() for now. + * If this driver acts as a gate, ehci/ohci-platform drivers don't need + * any modification. + */ +static const struct clk_ops usb2_clock_sel_clock_ops = { + .enable = usb2_clock_sel_enable, + .disable = usb2_clock_sel_disable, +};So you do "smart muxing" in the enable routine above?
Yes.
quoted
+static int __init rcar_usb2_clock_sel_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct usb2_clock_sel_priv *priv; + struct resource *res; + struct clk *clk; + struct clk_init_data init; + int error; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->ehci_clk = devm_clk_get(dev, "ehci_ohci"); + if (!IS_ERR(priv->ehci_clk)) { + error = clk_prepare_enable(priv->ehci_clk); + if (error) + return error; + } + + clk = devm_clk_get(dev, "usb_extal"); + if (!IS_ERR(clk) && !clk_prepare_enable(clk)) { + priv->extal = !!clk_get_rate(clk); + clk_disable_unprepare(clk); + } + clk = devm_clk_get(dev, "usb_xtal"); + if (!IS_ERR(clk) && !clk_prepare_enable(clk)) { + priv->xtal = !!clk_get_rate(clk); + clk_disable_unprepare(clk); + } + + if (!priv->extal && !priv->extal) { + dev_err(dev, "This driver needs usb_extal or usb_xtal\n"); + return -ENOENT;Perhaps enabled clocks should be disabled on error?
Oops! I should call clk_disable_unprepare(priv->ehci_clk) here. I will fix it.
quoted
+ } + + platform_set_drvdata(pdev, priv); + dev_set_drvdata(dev, priv); + + init.name = "rcar_usb2_clock_sel"; + init.ops = &usb2_clock_sel_clock_ops; + init.flags = CLK_IS_BASIC; + init.parent_names = NULL; + init.num_parents = 0; + priv->hw.init = &init; + + clk = clk_register(NULL, &priv->hw); + if (IS_ERR(clk)) + return PTR_ERR(clk);Likewise.
Same here. So, I will use "goto" for it.
quoted
+ error = of_clk_add_provider(np, of_clk_src_simple_get, clk); + if (error) + return error; + + return 0;return of_clk_add_provider(np, of_clk_src_simple_get, clk);
Thanks. I will fix it.
quoted
+/* Since this driver needs other ccf drivers, this uses subsys_initcall_sync */ +subsys_initcall_sync(rcar_usb2_clock_sel_init);I suppose this is a workaround for the lack of probe deferral support in the USB subsystem?
This is my fault. I added "power-domains" property into this device's node. After I remove the power-domains like the cpg node, this driver can use subsys_initcall() instead of subsys_initcall_sync(). Best regards, Yoshihiro Shimoda
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds