From: Balaji Prakash J bjagadee@codeaurora.org
Set reference clock period when it differs from dwc3 default hardware set.
We could calculate clock period based on reference clock frequency. But this information is not always available. This is the case of PCI bus attached USB host. For that reason we use a custom property.
Tested (USB2 only) on IPQ6010 SoC based board with 24 MHz reference clock while hardware default is 19.2 MHz.
[ baruch: rewrite commit message; drop GFLADJ code; remove 'quirk-' from property name; mention tested hardware ]
Acked-by: Felipe Balbi balbi@kernel.org Signed-off-by: Balaji Prakash J bjagadee@codeaurora.org Signed-off-by: Baruch Siach baruch@tkos.co.il Nacked-by: Rob Herring robh@kernel.org Link: https://lore.kernel.org/r/9f399bdf1ff752e31ab7497e3d5ce19bbb3ff247.163038945... Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org --- drivers/usb/dwc3/core.c | 29 +++++++++++++++++++++++++++++ drivers/usb/dwc3/core.h | 6 ++++++ 2 files changed, 35 insertions(+)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 6377b9cf81a5..7908c151b95d 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -26,6 +26,7 @@ #include <linux/acpi.h> #include <linux/pinctrl/consumer.h> #include <linux/reset.h> +#include <linux/bitfield.h>
#include <linux/usb/ch9.h> #include <linux/usb/gadget.h> @@ -343,6 +344,29 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc) } }
+/** + * dwc3_ref_clk_period - Reference clock period configuration + * Default reference clock period depends on hardware + * configuration. For systems with reference clock that differs + * from the default, this will set clock period in DWC3_GUCTL + * register. + * @dwc: Pointer to our controller context structure + * @ref_clk_per: reference clock period in ns + */ +static void dwc3_ref_clk_period(struct dwc3 *dwc) +{ + u32 reg; + + if (dwc->ref_clk_per == 0) + return; + + reg = dwc3_readl(dwc->regs, DWC3_GUCTL); + reg &= ~DWC3_GUCTL_REFCLKPER_MASK; + reg |= FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, dwc->ref_clk_per); + dwc3_writel(dwc->regs, DWC3_GUCTL, reg); +} + + /** * dwc3_free_one_event_buffer - Frees one event buffer * @dwc: Pointer to our controller context structure @@ -1021,6 +1045,9 @@ static int dwc3_core_init(struct dwc3 *dwc) /* Adjust Frame Length */ dwc3_frame_length_adjustment(dwc);
+ /* Adjust Reference Clock Period */ + dwc3_ref_clk_period(dwc); + dwc3_set_incr_burst_type(dwc);
usb_phy_set_suspend(dwc->usb2_phy, 0); @@ -1404,6 +1431,8 @@ static void dwc3_get_properties(struct dwc3 *dwc) &dwc->hsphy_interface); device_property_read_u32(dev, "snps,quirk-frame-length-adjustment", &dwc->fladj); + device_property_read_u32(dev, "snps,ref-clock-period-ns", + &dwc->ref_clk_per);
dwc->dis_metastability_quirk = device_property_read_bool(dev, "snps,dis_metastability_quirk"); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 3dcb5b744f7c..968608bd98e3 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -385,6 +385,10 @@ #define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7) #define DWC3_GFLADJ_30MHZ_MASK 0x3f
+/* Global User Control Register*/ +#define DWC3_GUCTL_REFCLKPER_MASK 0xffc00000 +#define DWC3_GUCTL_REFCLKPER_SEL 22 + /* Global User Control Register 2 */ #define DWC3_GUCTL2_RST_ACTBITLATER BIT(14)
@@ -969,6 +973,7 @@ struct dwc3_scratchpad_array { * @regs: base address for our registers * @regs_size: address space size * @fladj: frame length adjustment + * @ref_clk_per: reference clock period configuration * @irq_gadget: peripheral controller's IRQ number * @otg_irq: IRQ number for OTG IRQs * @current_otg_role: current role of operation while using the OTG block @@ -1153,6 +1158,7 @@ struct dwc3 { struct power_supply *usb_psy;
u32 fladj; + u32 ref_clk_per; u32 irq_gadget; u32 otg_irq; u32 current_otg_role;
From: Sean Anderson sean.anderson@seco.com
Instead of grabbing all clocks in bulk, grab them individually. This will allow us to get the frequency or otherwise deal with discrete clocks. This may break some platforms if they use a clock which doesn't use one of the documented names.
Reviewed-by: Robert Hancock robert.hancock@calian.com Signed-off-by: Sean Anderson sean.anderson@seco.com Link: https://lore.kernel.org/r/20220127200636.1456175-3-sean.anderson@seco.com Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org --- drivers/usb/dwc3/core.c | 64 ++++++++++++++++++++++++++++++++--------- drivers/usb/dwc3/core.h | 10 ++++--- 2 files changed, 57 insertions(+), 17 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 7908c151b95d..9635c915e2f7 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -753,6 +753,38 @@ static int dwc3_phy_setup(struct dwc3 *dwc) return 0; }
+static int dwc3_clk_enable(struct dwc3 *dwc) +{ + int ret; + + ret = clk_prepare_enable(dwc->bus_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(dwc->ref_clk); + if (ret) + goto disable_bus_clk; + + ret = clk_prepare_enable(dwc->susp_clk); + if (ret) + goto disable_ref_clk; + + return 0; + +disable_ref_clk: + clk_disable_unprepare(dwc->ref_clk); +disable_bus_clk: + clk_disable_unprepare(dwc->bus_clk); + return ret; +} + +static void dwc3_clk_disable(struct dwc3 *dwc) +{ + clk_disable_unprepare(dwc->susp_clk); + clk_disable_unprepare(dwc->ref_clk); + clk_disable_unprepare(dwc->bus_clk); +} + static void dwc3_core_exit(struct dwc3 *dwc) { dwc3_event_buffers_cleanup(dwc); @@ -767,7 +799,7 @@ static void dwc3_core_exit(struct dwc3 *dwc) phy_exit(dwc->usb2_generic_phy); phy_exit(dwc->usb3_generic_phy);
- clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks); + dwc3_clk_disable(dwc); reset_control_assert(dwc->reset); }
@@ -1614,25 +1646,31 @@ static int dwc3_probe(struct platform_device *pdev) return PTR_ERR(dwc->reset);
if (dev->of_node) { - ret = devm_clk_bulk_get_all(dev, &dwc->clks); - if (ret == -EPROBE_DEFER) - return ret; /* * Clocks are optional, but new DT platforms should support all * clocks as required by the DT-binding. */ - if (ret < 0) - dwc->num_clks = 0; - else - dwc->num_clks = ret; - + dwc->bus_clk = devm_clk_get_optional(dev, "bus_early"); + if (IS_ERR(dwc->bus_clk)) + return dev_err_probe(dev, PTR_ERR(dwc->bus_clk), + "could not get bus clock\n"); + + dwc->ref_clk = devm_clk_get_optional(dev, "ref"); + if (IS_ERR(dwc->ref_clk)) + return dev_err_probe(dev, PTR_ERR(dwc->ref_clk), + "could not get ref clock\n"); + + dwc->susp_clk = devm_clk_get_optional(dev, "suspend"); + if (IS_ERR(dwc->susp_clk)) + return dev_err_probe(dev, PTR_ERR(dwc->susp_clk), + "could not get suspend clock\n"); }
ret = reset_control_deassert(dwc->reset); if (ret) return ret;
- ret = clk_bulk_prepare_enable(dwc->num_clks, dwc->clks); + ret = dwc3_clk_enable(dwc); if (ret) goto assert_reset;
@@ -1723,7 +1761,7 @@ static int dwc3_probe(struct platform_device *pdev) pm_runtime_set_suspended(dev); pm_runtime_put_noidle(dev); disable_clks: - clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks); + dwc3_clk_disable(dwc->num_clks, dwc->clks); assert_reset: reset_control_assert(dwc->reset);
@@ -1773,7 +1811,7 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc) if (ret) return ret;
- ret = clk_bulk_prepare_enable(dwc->num_clks, dwc->clks); + ret = dwc3_clk_enable(dwc); if (ret) goto assert_reset;
@@ -1784,7 +1822,7 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc) return 0;
disable_clks: - clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks); + dwc3_clk_disable(dwc); assert_reset: reset_control_assert(dwc->reset);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 968608bd98e3..1e5ef1c512fb 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -967,8 +967,9 @@ struct dwc3_scratchpad_array { * @eps: endpoint array * @gadget: device side representation of the peripheral controller * @gadget_driver: pointer to the gadget driver - * @clks: array of clocks - * @num_clks: number of clocks + * @bus_clk: clock for accessing the registers + * @ref_clk: reference clock + * @susp_clk: clock used when the SS phy is in low power (S3) state * @reset: reset control * @regs: base address for our registers * @regs_size: address space size @@ -1127,8 +1128,9 @@ struct dwc3 { struct usb_gadget *gadget; struct usb_gadget_driver *gadget_driver;
- struct clk_bulk_data *clks; - int num_clks; + struct clk *bus_clk; + struct clk *ref_clk; + struct clk *susp_clk;
struct reset_control *reset;
From: Sean Anderson sean.anderson@seco.com
Instead of using a special property to determine the reference clock period, use the rate of the reference clock. When we have a legacy snps,ref-clock-period-ns property and no reference clock, use it instead. Fractional clocks are not currently supported, and will be dealt with in the next commit.
Tested-by: Robert Hancock robert.hancock@calian.com Reviewed-by: Robert Hancock robert.hancock@calian.com Reviewed-by: Thinh Nguyen Thinh.Nguyen@synopsys.com Signed-off-by: Sean Anderson sean.anderson@seco.com Link: https://lore.kernel.org/r/20220127200636.1456175-4-sean.anderson@seco.com Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org --- drivers/usb/dwc3/core.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 9635c915e2f7..6ee75a72c4d8 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -355,14 +355,24 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc) */ static void dwc3_ref_clk_period(struct dwc3 *dwc) { + unsigned long period; + unsigned long rate; u32 reg;
- if (dwc->ref_clk_per == 0) + if (dwc->ref_clk) { + rate = clk_get_rate(dwc->ref_clk); + if (!rate) + return; + period = NSEC_PER_SEC / rate; + } else if (dwc->ref_clk_per) { + period = dwc->ref_clk_per; + } else { return; + }
reg = dwc3_readl(dwc->regs, DWC3_GUCTL); reg &= ~DWC3_GUCTL_REFCLKPER_MASK; - reg |= FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, dwc->ref_clk_per); + reg |= FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, period); dwc3_writel(dwc->regs, DWC3_GUCTL, reg); }
From: Sean Anderson sean.anderson@seco.com
GUCTL.REFCLKPER can only account for clock frequencies with integer periods. To address this, program REFCLK_FLADJ with the relative error caused by period truncation. The formula given in the register reference has been rearranged to allow calculation based on rate (instead of period), and to allow for fixed-point arithmetic.
Additionally, calculate a value for 240MHZDECR. This configures a simulated 240Mhz clock using a counter with one fractional bit (PLS1).
This register is programmed only for versions >= 2.50a, since this is the check also used by commit db2be4e9e30c ("usb: dwc3: Add frame length adjustment quirk").
Tested-by: Robert Hancock robert.hancock@calian.com Reviewed-by: Robert Hancock robert.hancock@calian.com Reviewed-by: Thinh Nguyen Thinh.Nguyen@synopsys.com Signed-off-by: Sean Anderson sean.anderson@seco.com Link: https://lore.kernel.org/r/20220127200636.1456175-5-sean.anderson@seco.com Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org --- drivers/usb/dwc3/core.c | 39 ++++++++++++++++++++++++++++++++++++++- drivers/usb/dwc3/core.h | 3 +++ 2 files changed, 41 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 6ee75a72c4d8..2eb27c60542a 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -356,6 +356,8 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc) static void dwc3_ref_clk_period(struct dwc3 *dwc) { unsigned long period; + unsigned long fladj; + unsigned long decr; unsigned long rate; u32 reg;
@@ -366,6 +368,7 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc) period = NSEC_PER_SEC / rate; } else if (dwc->ref_clk_per) { period = dwc->ref_clk_per; + rate = NSEC_PER_SEC / period; } else { return; } @@ -374,8 +377,42 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc) reg &= ~DWC3_GUCTL_REFCLKPER_MASK; reg |= FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, period); dwc3_writel(dwc->regs, DWC3_GUCTL, reg); -}
+ if (DWC3_VER_IS_PRIOR(DWC3, 250A)) + return; + + /* + * The calculation below is + * + * 125000 * (NSEC_PER_SEC / (rate * period) - 1) + * + * but rearranged for fixed-point arithmetic. The division must be + * 64-bit because 125000 * NSEC_PER_SEC doesn't fit in 32 bits (and + * neither does rate * period). + * + * Note that rate * period ~= NSEC_PER_SECOND, minus the number of + * nanoseconds of error caused by the truncation which happened during + * the division when calculating rate or period (whichever one was + * derived from the other). We first calculate the relative error, then + * scale it to units of 8 ppm. + */ + fladj = div64_u64(125000ULL * NSEC_PER_SEC, (u64)rate * period); + fladj -= 125000; + + /* + * The documented 240MHz constant is scaled by 2 to get PLS1 as well. + */ + decr = 480000000 / rate; + + reg = dwc3_readl(dwc->regs, DWC3_GFLADJ); + reg &= ~DWC3_GFLADJ_REFCLK_FLADJ_MASK + & ~DWC3_GFLADJ_240MHZDECR + & ~DWC3_GFLADJ_240MHZDECR_PLS1; + reg |= FIELD_PREP(DWC3_GFLADJ_REFCLK_FLADJ_MASK, fladj) + | FIELD_PREP(DWC3_GFLADJ_240MHZDECR, decr >> 1) + | FIELD_PREP(DWC3_GFLADJ_240MHZDECR_PLS1, decr & 1); + dwc3_writel(dwc->regs, DWC3_GFLADJ, reg); +}
/** * dwc3_free_one_event_buffer - Frees one event buffer diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 1e5ef1c512fb..9c421de4d01d 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -384,6 +384,9 @@ /* Global Frame Length Adjustment Register */ #define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7) #define DWC3_GFLADJ_30MHZ_MASK 0x3f +#define DWC3_GFLADJ_REFCLK_FLADJ_MASK GENMASK(21, 8) +#define DWC3_GFLADJ_240MHZDECR GENMASK(30, 24) +#define DWC3_GFLADJ_240MHZDECR_PLS1 BIT(31)
/* Global User Control Register*/ #define DWC3_GUCTL_REFCLKPER_MASK 0xffc00000
From: Alexander Stein alexander.stein@ew.tq-group.com
This selects the SOF/ITP counter be running on ref_clk. As documented U2_FREECLK_EXISTS has to be set to 0 as well.
Reviewed-by: Li Jun jun.li@nxp.com Signed-off-by: Alexander Stein alexander.stein@ew.tq-group.com Link: https://lore.kernel.org/r/20220915062855.751881-3-alexander.stein@ew.tq-grou... Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org --- drivers/usb/dwc3/core.c | 8 +++++++- drivers/usb/dwc3/core.h | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 2eb27c60542a..c065d165bedd 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -411,6 +411,10 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc) reg |= FIELD_PREP(DWC3_GFLADJ_REFCLK_FLADJ_MASK, fladj) | FIELD_PREP(DWC3_GFLADJ_240MHZDECR, decr >> 1) | FIELD_PREP(DWC3_GFLADJ_240MHZDECR_PLS1, decr & 1); + + if (dwc->gfladj_refclk_lpm_sel) + reg |= DWC3_GFLADJ_REFCLK_LPM_SEL; + dwc3_writel(dwc->regs, DWC3_GFLADJ, reg); }
@@ -792,7 +796,7 @@ static int dwc3_phy_setup(struct dwc3 *dwc) else reg |= DWC3_GUSB2PHYCFG_ENBLSLPM;
- if (dwc->dis_u2_freeclk_exists_quirk) + if (dwc->dis_u2_freeclk_exists_quirk || dwc->gfladj_refclk_lpm_sel) reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); @@ -1501,6 +1505,8 @@ static void dwc3_get_properties(struct dwc3 *dwc) "snps,resume-hs-terminations"); dwc->parkmode_disable_ss_quirk = device_property_read_bool(dev, "snps,parkmode-disable-ss-quirk"); + dwc->gfladj_refclk_lpm_sel = device_property_read_bool(dev, + "snps,gfladj-refclk-lpm-sel-quirk");
dwc->tx_de_emphasis_quirk = device_property_read_bool(dev, "snps,tx_de_emphasis_quirk"); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 9c421de4d01d..d4004bab4cc5 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -385,6 +385,7 @@ #define DWC3_GFLADJ_30MHZ_SDBND_SEL BIT(7) #define DWC3_GFLADJ_30MHZ_MASK 0x3f #define DWC3_GFLADJ_REFCLK_FLADJ_MASK GENMASK(21, 8) +#define DWC3_GFLADJ_REFCLK_LPM_SEL BIT(23) #define DWC3_GFLADJ_240MHZDECR GENMASK(30, 24) #define DWC3_GFLADJ_240MHZDECR_PLS1 BIT(31)
@@ -1299,6 +1300,7 @@ struct dwc3 { unsigned dis_tx_ipgap_linecheck_quirk:1; unsigned resume_hs_terminations:1; unsigned parkmode_disable_ss_quirk:1; + unsigned gfladj_refclk_lpm_sel:1;
unsigned tx_de_emphasis_quirk:1; unsigned tx_de_emphasis:2;
Hi,
Thanks for your patch.
FYI: kernel test robot notices the stable kernel rule is not satisfied.
The check is based on https://www.kernel.org/doc/html/latest/process/stable-kernel-rules.html/#opt...
Rule: add the tag "Cc: stable@vger.kernel.org" in the sign-off area to have the patch automatically included in the stable tree. Subject: [PATCH 1/5] usb: dwc3: reference clock period configuration Link: https://lore.kernel.org/stable/20230904071432.32309-1-tomasz.rostanski%40tha...
On Mon, Sep 04, 2023 at 09:14:22AM +0200, Tomasz Rostanski wrote:
From: Balaji Prakash J bjagadee@codeaurora.org
Set reference clock period when it differs from dwc3 default hardware set.
We could calculate clock period based on reference clock frequency. But this information is not always available. This is the case of PCI bus attached USB host. For that reason we use a custom property.
Tested (USB2 only) on IPQ6010 SoC based board with 24 MHz reference clock while hardware default is 19.2 MHz.
[ baruch: rewrite commit message; drop GFLADJ code; remove 'quirk-' from property name; mention tested hardware ]
Acked-by: Felipe Balbi balbi@kernel.org Signed-off-by: Balaji Prakash J bjagadee@codeaurora.org Signed-off-by: Baruch Siach baruch@tkos.co.il Nacked-by: Rob Herring robh@kernel.org Link: https://lore.kernel.org/r/9f399bdf1ff752e31ab7497e3d5ce19bbb3ff247.163038945... Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org
drivers/usb/dwc3/core.c | 29 +++++++++++++++++++++++++++++ drivers/usb/dwc3/core.h | 6 ++++++ 2 files changed, 35 insertions(+)
What is the git commit id of this change (and all the others in this series?)
What stable tree(s) are you wanting this to be applied to?
And you forgot to sign off on the patches as well, that is required.
thanks,
greg k-h
linux-stable-mirror@lists.linaro.org