[PATCH 06/14] staging: clocking-wizard: Swap CCF clock providers
Replace existing CCF clock providers with new clock provider that can be enhanced to meet our needs. AXI clock prepare/enable/disable/unprepare is now managed by regmap APIs. Unregistering of clk instances now handled by devm APIs. Drop warning that fractional ratios are not supported because it used undocumented register fields and it won't be needed anymore. Signed-off-by: James Kelly --- .../clocking-wizard/clk-xlnx-clock-wizard.c| 211 +++-- 1 file changed, 71 insertions(+), 140 deletions(-) diff --git a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c index ba9b1dc93d50..1dbeda92fb9a 100644 --- a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c +++ b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c @@ -76,24 +76,6 @@ #define WZRD_CLKNAME_IN1 "clk_in1" #define WZRD_FLAG_MULTIPLY BIT(0) -#define WZRD_CLK_CFG_REG(n)(0x200 + 4 * (n)) - -#define WZRD_CLKOUT0_FRAC_EN BIT(18) -#define WZRD_CLKFBOUT_FRAC_EN BIT(26) - -#define WZRD_CLKFBOUT_MULT_SHIFT 8 -#define WZRD_CLKFBOUT_MULT_MASK(0xff << WZRD_CLKFBOUT_MULT_SHIFT) -#define WZRD_DIVCLK_DIVIDE_SHIFT 0 -#define WZRD_DIVCLK_DIVIDE_MASK(0xff << WZRD_DIVCLK_DIVIDE_SHIFT) -#define WZRD_CLKOUT_DIVIDE_SHIFT 0 -#define WZRD_CLKOUT_DIVIDE_MASK(0xff << WZRD_DIVCLK_DIVIDE_SHIFT) - -enum clk_wzrd_int_clks { - wzrd_clk_mul_div, - wzrd_clk_mul, - wzrd_clk_int_max -}; - enum clk_wzrd_clk { WZRD_CLK_DIV, WZRD_CLK_PLL, @@ -149,14 +131,13 @@ struct clk_wzrd_clk_data { container_of(_hw, struct clk_wzrd_clk_data, hw) /** - * struct clk_wzrd: - * @clk_data: Clock data + * struct clk_wzrd - Platform driver data for clocking wizard device + * + * @clk_data: Clock data accessible to other devices via device tree * @nb:Notifier block - * @base: Memory base * @regmap:Register map for hardware * @clk_in1: Handle to input clock 'clk_in1' * @axi_clk: Handle to input clock 's_axi_aclk' - * @clks_internal: Internal clocks * @clkout:Output clocks * @speed_grade: Speed grade of the device * @suspended: Flag indicating power state of the device @@ -167,11 +148,9 @@ struct clk_wzrd_clk_data { struct clk_wzrd { struct clk_onecell_data clk_data; struct notifier_block nb; - void __iomem*base; struct regmap *regmap; struct clk *clk_in1; struct clk *axi_clk; - struct clk *clks_internal[wzrd_clk_int_max]; struct clk *clkout[WZRD_NUM_OUTPUTS]; unsigned intspeed_grade; boolsuspended; @@ -179,7 +158,6 @@ struct clk_wzrd { struct clk_wzrd_clk_datapll_data; struct clk_wzrd_clk_dataclkout_data[0]; }; - #define to_clk_wzrd(_nb) container_of(_nb, struct clk_wzrd, nb) /* maximum frequencies for input/output clocks per speed grade */ @@ -321,7 +299,6 @@ static int __maybe_unused clk_wzrd_suspend(struct device *dev) { struct clk_wzrd *cw = dev_get_drvdata(dev); - clk_disable_unprepare(cw->axi_clk); cw->suspended = true; return 0; @@ -329,15 +306,8 @@ static int __maybe_unused clk_wzrd_suspend(struct device *dev) static int __maybe_unused clk_wzrd_resume(struct device *dev) { - int ret; struct clk_wzrd *cw = dev_get_drvdata(dev); - ret = clk_prepare_enable(cw->axi_clk); - if (ret) { - dev_err(dev, "unable to enable s_axi_aclk\n"); - return ret; - } - cw->suspended = false; return 0; @@ -348,17 +318,32 @@ static SIMPLE_DEV_PM_OPS(clk_wzrd_dev_pm_ops, clk_wzrd_suspend, static int clk_wzrd_get_device_tree_data(struct device *dev) { - int ret; - unsigned long rate; + int num_outputs, ret; struct clk_wzrd *cw; + struct device_node *node = dev->of_node; + + num_outputs = of_property_count_strings(node, + "clock-output-names"); + if (num_outputs < 1) { + dev_err(dev, "No clock output names in device tree\n"); + if (num_outputs < 0) + return num_outputs; + else + return -EINVAL; + } + if (num_outputs > WZRD_NUM_OUTPUTS) { + dev_warn(dev, "Too many clock output names in device tree\n"); + num_outputs = WZRD_NUM_OUTPUTS; + } - cw = devm_kzalloc(dev, sizeof(*cw), GFP_KERNEL);
[PATCH 05/14] staging: clocking-wizard: Implement CCF clock provider
The CCF clock providers that are currently used by the driver are not capable of supporting the Clocking Wizard IP register interface for fractional ratios, nor are they able to enforce constraints require to ensure the PLL will always lock. None of the common CCF clock providers seem to be a good fit so we implement a new CCF clock provider within the driver that can be expanded upon in subsequent patches. The initial implementation supports multiply or divide by fixed integer ratios. The CCF clock provider uses: - devm kernel APIs for clock registration to simplify error recovery and module unloading. - regmap kernel APIs for memory mapped I/O. Regmap is also able to manage prepare/enable/disable/unprepare of the AXI clock for us. Note that use of the new CCF clock provider is deferred to a subsequent patch. Signed-off-by: James Kelly --- .../clocking-wizard/clk-xlnx-clock-wizard.c| 164 + 1 file changed, 164 insertions(+) diff --git a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c index 3b66ac3b5ed0..ba9b1dc93d50 100644 --- a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c +++ b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c @@ -3,6 +3,7 @@ * Xilinx 'Clocking Wizard' driver * * Copyright (C) 2013 - 2014 Xilinx + * Copyright (C) 2018 James Kelly * * Sören Brinkmann * @@ -67,11 +68,13 @@ #include #include #include +#include #define WZRD_NUM_OUTPUTS 7 #define WZRD_ACLK_MAX_FREQ 25000UL #define WZRD_CLKNAME_AXI "s_axi_aclk" #define WZRD_CLKNAME_IN1 "clk_in1" +#define WZRD_FLAG_MULTIPLY BIT(0) #define WZRD_CLK_CFG_REG(n)(0x200 + 4 * (n)) @@ -91,28 +94,90 @@ enum clk_wzrd_int_clks { wzrd_clk_int_max }; +enum clk_wzrd_clk { + WZRD_CLK_DIV, + WZRD_CLK_PLL, + WZRD_CLK_OUT, + WZRD_CLK_COUNT +}; + +/* + * Register map extracted from Xilinx document listed below. + * + * PG065 Clocking Wizard v6.0 LogiCORE IP Product Guide + */ +static const struct regmap_config clk_wzrd_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32 +}; + +static const struct reg_field clk_wzrd_status_locked = REG_FIELD(0x004, 0, 0); +static const struct reg_field clk_wzrd_divclk_divide = REG_FIELD(0x200, 0, 7); +static const struct reg_field clk_wzrd_clkfbout_mult = REG_FIELD(0x200, 8, 15); +static const struct reg_field clk_wzrd_clkfbout_frac = REG_FIELD(0x200, 16, 25); +static const struct reg_field clk_wzrd_clkout_divide[WZRD_NUM_OUTPUTS] = { + REG_FIELD(0x208, 0, 7), + REG_FIELD(0x214, 0, 7), + REG_FIELD(0x220, 0, 7), + REG_FIELD(0x22C, 0, 7), + REG_FIELD(0x238, 0, 7), + REG_FIELD(0x244, 0, 7), + REG_FIELD(0x250, 0, 7) +}; + +static const struct reg_field clk_wzrd_clkout0_frac = REG_FIELD(0x208, 8, 17); +static const struct reg_field clk_wzrd_reconfig = REG_FIELD(0x25C, 0, 1); + +/** + * struct clk_wzrd_clk_data - Clocking Wizard component clock provider data + * + * @hw:handle between common and hardware-specific interfaces + * @flags: hardware specific flags + * @int_field: pointer to regmap field for integer part + * + * Flags: + * WZRD_FLAG_MULTIPLY Clock is a multiplier rather than a divider + */ +struct clk_wzrd_clk_data { + struct clk_hw hw; + unsigned long flags; + struct regmap_field *int_field; +}; + +#define to_clk_wzrd_clk_data(_hw) \ + container_of(_hw, struct clk_wzrd_clk_data, hw) + /** * struct clk_wzrd: * @clk_data: Clock data * @nb:Notifier block * @base: Memory base + * @regmap:Register map for hardware * @clk_in1: Handle to input clock 'clk_in1' * @axi_clk: Handle to input clock 's_axi_aclk' * @clks_internal: Internal clocks * @clkout:Output clocks * @speed_grade: Speed grade of the device * @suspended: Flag indicating power state of the device + * @div: Divider internal clock provider data + * @pll: Phase locked loop internal clock provider data + * @clkout_data: Array of output clock provider data */ struct clk_wzrd { struct clk_onecell_data clk_data; struct notifier_block nb; void __iomem*base; + struct regmap *regmap; struct clk *clk_in1; struct clk *axi_clk; struct clk *clks_internal[wzrd_clk_int_max]; struct clk *clkout[WZRD_NUM_OUTPUTS]; unsigned intspeed_grade; boolsuspended; + struct
[PATCH 01/14] staging: clocking-wizard: Add principles of operation
Add a description for how the Xilinx Clocking Wizard IP works to guide subsequent patches. Signed-off-by: James Kelly --- .../clocking-wizard/clk-xlnx-clock-wizard.c| 52 ++ 1 file changed, 52 insertions(+) diff --git a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c index cae7e6e695b0..babbed42f96d 100644 --- a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c +++ b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c @@ -5,6 +5,58 @@ * Copyright (C) 2013 - 2014 Xilinx * * Sören Brinkmann + * + * Principles of Operation: + * + * The Xilinx clocking wizard IP implements a clock complex that can be + * modelled as a collection of dividers and a PLL multiplier arranged in + * the following configuration: + * + * +---> clk_fbout + * | + * fin +-+ |+-+ vco +-+ + * clk_in1 ->| DIV |---+--->| PLL |---+--->| DIV |-> clk_out1 + * +-+ pfd +-+ |+-+ + *| + *|+-+ + *+--->| DIV |-> clk_out2 + *|+-+ + *| + *| ... + *|+-+ + *+--->| DIV |-> clk_outn + * +-+ + * + * Each divider and the PLL multiplier correspond to a distinct common + * clock framework struct clk. + * + * The number of clock outputs depends the clock primitive type (MMCM or PLL) + * and FPGA family and can range from 2 to 7, not including clk_fbout. + * Xilinx documentation is inconsistent in the numbering of these outputs. + * The clocking wizard uses 1 thru n whereas the clocking primitives wrapped + * by the wizard use 0 through n-1. + * + * This driver publishes the n output clocks in the device tree using addresses + * 0 through n-1. The remaining two clocks (DIV and PLL) are not published in + * the device tree but can be obtained using calls to clk_get_parent on one + * of the output clocks. + * + * There are constraints on the input rate (fin), phase-frequency + * detector rate (pfd), the voltage controlled oscillator rate (vco) + * and output clock rates. These depend on FPGA family, clock primitive type + * and chip speed grade. + * + * The available ratios for the dividers and PLL multiplier depend on + * FPGA family and clock primitive type. MMCM primitves support fractional + * ratios for the PLL multipler and first output divider, whereas PLL + * primitives do not. Fractional ratios have a resolution of 0.125 (1/8) or 3 + * fractional bits. + * + * Clock ratios can be dynamically changed via two different register + * interfaces depending on how the "Write to DRP" configuration option is set + * when the clocking wizard IP is customized. This driver requires that + * the "Write to DRP" configuration option is disabled in customization + * as it currently uses the higher-level of the two register interfaces. */ #include -- 2.15.1 (Apple Git-101) ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH 03/14] staging: clocking-wizard: Split probe function
Split probe function so that we can add more code in future patches without it becoming too big. Signed-off-by: James Kelly --- .../clocking-wizard/clk-xlnx-clock-wizard.c| 63 +- 1 file changed, 51 insertions(+), 12 deletions(-) diff --git a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c index 1d42eabdd956..4dec1bfc303a 100644 --- a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c +++ b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c @@ -180,14 +180,12 @@ static int __maybe_unused clk_wzrd_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(clk_wzrd_dev_pm_ops, clk_wzrd_suspend, clk_wzrd_resume); -static int clk_wzrd_probe(struct platform_device *pdev) +static int clk_wzrd_get_device_tree_data(struct device *dev) { - int i, ret; - u32 reg; + int ret; unsigned long rate; - const char *clk_name; struct clk_wzrd *clk_wzrd; - struct resource *mem; + struct platform_device *pdev = to_platform_device(dev); struct device_node *np = pdev->dev.of_node; clk_wzrd = devm_kzalloc(&pdev->dev, sizeof(*clk_wzrd), GFP_KERNEL); @@ -195,11 +193,6 @@ static int clk_wzrd_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, clk_wzrd); - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - clk_wzrd->base = devm_ioremap_resource(&pdev->dev, mem); - if (IS_ERR(clk_wzrd->base)) - return PTR_ERR(clk_wzrd->base); - ret = of_property_read_u32(np, "speed-grade", &clk_wzrd->speed_grade); if (!ret) { if (clk_wzrd->speed_grade < 1 || clk_wzrd->speed_grade > 3) { @@ -231,10 +224,36 @@ static int clk_wzrd_probe(struct platform_device *pdev) if (rate > WZRD_ACLK_MAX_FREQ) { dev_err(&pdev->dev, "s_axi_aclk frequency (%lu) too high\n", rate); - ret = -EINVAL; - goto err_disable_clk; + clk_disable_unprepare(clk_wzrd->axi_clk); + return -EINVAL; } + return 0; +} + +static int clk_wzrd_regmap_alloc(struct device *dev) +{ + struct resource *mem; + struct clk_wzrd *clk_wzrd = dev_get_drvdata(dev); + struct platform_device *pdev = to_platform_device(dev); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + clk_wzrd->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(clk_wzrd->base)) + return PTR_ERR(clk_wzrd->base); + + return 0; +} + +static int clk_wzrd_register_ccf(struct device *dev) +{ + int i, ret; + u32 reg; + const char *clk_name; + struct platform_device *pdev = to_platform_device(dev); + struct clk_wzrd *clk_wzrd = dev_get_drvdata(dev); + struct device_node *np = pdev->dev.of_node; + /* we don't support fractional div/mul yet */ reg = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(0)) & WZRD_CLKFBOUT_FRAC_EN; @@ -342,6 +361,26 @@ static int clk_wzrd_probe(struct platform_device *pdev) return ret; } +static int clk_wzrd_probe(struct platform_device *pdev) +{ + int ret; + struct device *dev = &pdev->dev; + + ret = clk_wzrd_get_device_tree_data(dev); + if (ret) + return ret; + + ret = clk_wzrd_regmap_alloc(dev); + if (ret) + return ret; + + ret = clk_wzrd_register_ccf(dev); + if (ret) + return ret; + + return 0; +} + static int clk_wzrd_remove(struct platform_device *pdev) { int i; -- 2.15.1 (Apple Git-101) ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH 00/14] staging: clocking-wizard: Implement many TODOs
This series of patches attempts to implement many of the outstanding TODOs for the Xilinx Clocking Wizard IP driver that has been languishing in the staging tree for some time. I had a need for this driver so I thought it appropriate to give it some love. Hopefully others will find these patches useful. Highlights include: - Support for fractional ratios when available in hardware. - Support for clk_round_rate and clk_set_rate kernel APIs. - Automatic set rate of internal clocks when rate of output clock is set. - Automatic set rate of input clock and internal clocks when rate of output clock is set. A CCF clock provider has been implemented within the driver to support the new functionality as it was not possible to do this with the existing clock providers. There is also code to handle a limitation of Clocking Wizard IP which prevents changes to the clock ratios if the PLL is not locked. Great care has to be taken to ensure the PLL will always lock as there is no way for the driver to recover if the PLL fails to lock under transient conditions that may drive the PLL VCO frequency out of range. The patches were built on the current staging-next branch. The patches also work with the xlnx_rebase_v4.14 branch of the Xilinx linux tree at https://github.com/Xilinx/linux-xlnx.git - this branch is used by the current release (2018.1) of the Xilinx development tools. Patches corresponding to the following staging tree commits are required as prerequisites before applying this patch series to xlnx_rebase_v4.14: 667063acb81931e2f8fd0cb91df9fcccad131d9a regmap: add iopoll-like polling macro for regmap_field 1dbb3344d9e9cd6e72da23f4058f3e6e926614b6 staging: clocking-wizard: add SPDX identifier 09956d59bad5f5bb238efb805f0767060e624378 staging: clocking-wizard: remove redundant license text a08f06bb7a0743a7fc8d571899c93d882468096e seq_file: Introduce DEFINE_SHOW_ATTRIBUTE() helper macro Testing has been done on a Digilent Zybo-Z7 development board. This uses a 32-bit ARM architecture Zynq-7020 SoC. Testing used the 2018.1 release of the Xilinx development tools which has v6.0 of the Clocking Wizard IP. The patches are also applicable to, but are currently untested on: - 64-bit ARM architecture (Zynq Ultrascale+ MPSoC etc.) - Microblaze architecture (7-Series, Ultrascale, Ultrascale+ FPGAs) Others with access to suitable hardware will need to test these platforms. Potential users of this driver may use the Xilinx device tree generator from https://github.com/Xilinx/device-tree-xlnx.git either directly or via the development tools provided by Xilinx. There are two issues with the DTG that any potential testers of these patches should be aware of: - The DTG generates a negative value for the device-tree speed-grade property. The 7th patch has a hack to handle this quirk until Xilinx fix their DTG. - The 2018.1 DTG changed the device-tree clock-output-names property so it no longer provides any information about how the Clocking Wizard IP is actually configured. These patches will still work with the new 2018.1 DTG behaviour but the names of the output clocks will no longer match those used in the Clocking Wizard IP customization, and the maximum number of clocks will always be created even if not used or FPGA. A warning will also be issued stating that there are too many clock output names. Further details can be found in the Xilinx Community forums at https://bit.ly/2jmFIRf. The original driver author appears to have left Xilinx so I have not included them as an addressee for these patches and instead directed them to another Xilinx Linux maintainer. James Kelly (14): staging: clocking-wizard: Add principles of operation staging: clocking-wizard: Reverse order of internal clocks staging: clocking-wizard: Split probe function staging: clocking-wizard: Cosmetic cleanups staging: clocking-wizard: Implement CCF clock provider staging: clocking-wizard: Swap CCF clock providers staging: clocking-wizard: Add hardware constaints staging: clocking-wizard: Support fractional ratios staging: clocking-wizard: Provide more information in debugfs staging: clocking-wizard: Support clk_round_rate staging: clocking-wizard: Support clk_set_rate staging: clocking-wizard: Automatically set internal clock rates staging: clocking-wizard: Automatically set input clock rate staging: clocking-wizard: Add debugfs entries to facilitate testing. drivers/staging/clocking-wizard/TODO |7 +- .../clocking-wizard/clk-xlnx-clock-wizard.c| 1406 +--- drivers/staging/clocking-wizard/dt-binding.txt | 19 +- 3 files changed, 1234 insertions(+), 198 deletions(-) -- 2.15.1 (Apple Git-101) ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH 04/14] staging: clocking-wizard: Cosmetic cleanups
Do some cosmetic cleanups before we start adding lots of code. - Shorten clk_wzrd identifier to cw to keep lines short - Replace &pdev->dev with dev to keep lines short - Remove convenience variable np as it was only used once in function - Add some tabs to make clk_wzrd structure definitions easier to read - Use #define for clock names in case we want to change them later Signed-off-by: James Kelly --- .../clocking-wizard/clk-xlnx-clock-wizard.c| 210 ++--- 1 file changed, 105 insertions(+), 105 deletions(-) diff --git a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c index 4dec1bfc303a..3b66ac3b5ed0 100644 --- a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c +++ b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c @@ -70,6 +70,8 @@ #define WZRD_NUM_OUTPUTS 7 #define WZRD_ACLK_MAX_FREQ 25000UL +#define WZRD_CLKNAME_AXI "s_axi_aclk" +#define WZRD_CLKNAME_IN1 "clk_in1" #define WZRD_CLK_CFG_REG(n)(0x200 + 4 * (n)) @@ -102,15 +104,15 @@ enum clk_wzrd_int_clks { * @suspended: Flag indicating power state of the device */ struct clk_wzrd { - struct clk_onecell_data clk_data; - struct notifier_block nb; - void __iomem *base; - struct clk *clk_in1; - struct clk *axi_clk; - struct clk *clks_internal[wzrd_clk_int_max]; - struct clk *clkout[WZRD_NUM_OUTPUTS]; - unsigned int speed_grade; - bool suspended; + struct clk_onecell_data clk_data; + struct notifier_block nb; + void __iomem*base; + struct clk *clk_in1; + struct clk *axi_clk; + struct clk *clks_internal[wzrd_clk_int_max]; + struct clk *clkout[WZRD_NUM_OUTPUTS]; + unsigned intspeed_grade; + boolsuspended; }; #define to_clk_wzrd(_nb) container_of(_nb, struct clk_wzrd, nb) @@ -127,14 +129,14 @@ static int clk_wzrd_clk_notifier(struct notifier_block *nb, unsigned long event, { unsigned long max; struct clk_notifier_data *ndata = data; - struct clk_wzrd *clk_wzrd = to_clk_wzrd(nb); + struct clk_wzrd *cw = to_clk_wzrd(nb); - if (clk_wzrd->suspended) + if (cw->suspended) return NOTIFY_OK; - if (ndata->clk == clk_wzrd->clk_in1) - max = clk_wzrd_max_freq[clk_wzrd->speed_grade - 1]; - else if (ndata->clk == clk_wzrd->axi_clk) + if (ndata->clk == cw->clk_in1) + max = clk_wzrd_max_freq[cw->speed_grade - 1]; + else if (ndata->clk == cw->axi_clk) max = WZRD_ACLK_MAX_FREQ; else return NOTIFY_DONE; /* should never happen */ @@ -153,10 +155,10 @@ static int clk_wzrd_clk_notifier(struct notifier_block *nb, unsigned long event, static int __maybe_unused clk_wzrd_suspend(struct device *dev) { - struct clk_wzrd *clk_wzrd = dev_get_drvdata(dev); + struct clk_wzrd *cw = dev_get_drvdata(dev); - clk_disable_unprepare(clk_wzrd->axi_clk); - clk_wzrd->suspended = true; + clk_disable_unprepare(cw->axi_clk); + cw->suspended = true; return 0; } @@ -164,15 +166,15 @@ static int __maybe_unused clk_wzrd_suspend(struct device *dev) static int __maybe_unused clk_wzrd_resume(struct device *dev) { int ret; - struct clk_wzrd *clk_wzrd = dev_get_drvdata(dev); + struct clk_wzrd *cw = dev_get_drvdata(dev); - ret = clk_prepare_enable(clk_wzrd->axi_clk); + ret = clk_prepare_enable(cw->axi_clk); if (ret) { dev_err(dev, "unable to enable s_axi_aclk\n"); return ret; } - clk_wzrd->suspended = false; + cw->suspended = false; return 0; } @@ -184,47 +186,46 @@ static int clk_wzrd_get_device_tree_data(struct device *dev) { int ret; unsigned long rate; - struct clk_wzrd *clk_wzrd; - struct platform_device *pdev = to_platform_device(dev); - struct device_node *np = pdev->dev.of_node; + struct clk_wzrd *cw; - clk_wzrd = devm_kzalloc(&pdev->dev, sizeof(*clk_wzrd), GFP_KERNEL); - if (!clk_wzrd) + cw = devm_kzalloc(dev, sizeof(*cw), GFP_KERNEL); + if (!cw) return -ENOMEM; - platform_set_drvdata(pdev, clk_wzrd); + dev_set_drvdata(dev, cw); - ret = of_property_read_u32(np, "speed-grade", &clk_wzrd->speed_grade); + ret = of_property_read_u32(dev->of_node, "speed-grade", + &cw->speed_grade); if (!ret) { - if (clk_wzrd->speed_grade < 1 || clk_wzrd
[PATCH 02/14] staging: clocking-wizard: Reverse order of internal clocks
The order of the internal clocks does not match how the hardware is arranged. We need to fix this before we can add new function. Swap the order of the internal multiplier and divider clocks so that the divider is the parent of the multiplier. Signed-off-by: James Kelly --- .../clocking-wizard/clk-xlnx-clock-wizard.c| 37 +++--- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c index babbed42f96d..1d42eabdd956 100644 --- a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c +++ b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c @@ -84,8 +84,8 @@ #define WZRD_CLKOUT_DIVIDE_MASK(0xff << WZRD_DIVCLK_DIVIDE_SHIFT) enum clk_wzrd_int_clks { - wzrd_clk_mul, wzrd_clk_mul_div, + wzrd_clk_mul, wzrd_clk_int_max }; @@ -243,41 +243,40 @@ static int clk_wzrd_probe(struct platform_device *pdev) if (reg) dev_warn(&pdev->dev, "fractional div/mul not supported\n"); - /* register multiplier */ + /* register div */ reg = (readl(clk_wzrd->base + WZRD_CLK_CFG_REG(0)) & -WZRD_CLKFBOUT_MULT_MASK) >> WZRD_CLKFBOUT_MULT_SHIFT; - clk_name = kasprintf(GFP_KERNEL, "%s_mul", dev_name(&pdev->dev)); + WZRD_DIVCLK_DIVIDE_MASK) >> WZRD_DIVCLK_DIVIDE_SHIFT; + clk_name = kasprintf(GFP_KERNEL, "%s_mul_div", dev_name(&pdev->dev)); if (!clk_name) { ret = -ENOMEM; goto err_disable_clk; } - clk_wzrd->clks_internal[wzrd_clk_mul] = clk_register_fixed_factor( + clk_wzrd->clks_internal[wzrd_clk_mul_div] = clk_register_fixed_factor( &pdev->dev, clk_name, __clk_get_name(clk_wzrd->clk_in1), - 0, reg, 1); + 0, 1, reg); kfree(clk_name); - if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul])) { - dev_err(&pdev->dev, "unable to register fixed-factor clock\n"); - ret = PTR_ERR(clk_wzrd->clks_internal[wzrd_clk_mul]); + if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul_div])) { + dev_err(&pdev->dev, "unable to register divider clock\n"); + ret = PTR_ERR(clk_wzrd->clks_internal[wzrd_clk_mul_div]); goto err_disable_clk; } - /* register div */ + /* register multiplier */ reg = (readl(clk_wzrd->base + WZRD_CLK_CFG_REG(0)) & - WZRD_DIVCLK_DIVIDE_MASK) >> WZRD_DIVCLK_DIVIDE_SHIFT; - clk_name = kasprintf(GFP_KERNEL, "%s_mul_div", dev_name(&pdev->dev)); +WZRD_CLKFBOUT_MULT_MASK) >> WZRD_CLKFBOUT_MULT_SHIFT; + clk_name = kasprintf(GFP_KERNEL, "%s_mul", dev_name(&pdev->dev)); if (!clk_name) { ret = -ENOMEM; goto err_rm_int_clk; } - - clk_wzrd->clks_internal[wzrd_clk_mul_div] = clk_register_fixed_factor( + clk_wzrd->clks_internal[wzrd_clk_mul] = clk_register_fixed_factor( &pdev->dev, clk_name, - __clk_get_name(clk_wzrd->clks_internal[wzrd_clk_mul]), - 0, 1, reg); - if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul_div])) { - dev_err(&pdev->dev, "unable to register divider clock\n"); - ret = PTR_ERR(clk_wzrd->clks_internal[wzrd_clk_mul_div]); + __clk_get_name(clk_wzrd->clks_internal[wzrd_clk_mul_div]), + 0, reg, 1); + if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul])) { + dev_err(&pdev->dev, "unable to register fixed-factor clock\n"); + ret = PTR_ERR(clk_wzrd->clks_internal[wzrd_clk_mul]); goto err_rm_int_clk; } -- 2.15.1 (Apple Git-101) ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH 14/14] staging: clocking-wizard: Add debugfs entries to facilitate testing.
Adds test_round_rate and test_set_rate entries to debugfs so that the driver can be tested by a user space test application. It would appear that patches that allow access to clk_set_rate from user space are controversial so there is no expectation that this patch can be merged. Signed-off-by: James Kelly --- .../clocking-wizard/clk-xlnx-clock-wizard.c| 53 ++ 1 file changed, 53 insertions(+) diff --git a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c index bb64da849d9b..c37f0e4451b4 100644 --- a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c +++ b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c @@ -308,6 +308,12 @@ static const struct reg_field clk_wzrd_clkout_divide[WZRD_MAX_OUTPUTS] = { static const struct reg_field clk_wzrd_clkout0_frac = REG_FIELD(0x208, 8, 17); static const struct reg_field clk_wzrd_reconfig = REG_FIELD(0x25C, 0, 1); +#ifdef CONFIG_DEBUG_FS +struct clk_wzrd_debug { + unsigned long round_rate; +}; +#endif + /** * struct clk_wzrd_clk_data - Clocking Wizard component clock provider data * @@ -319,6 +325,7 @@ static const struct reg_field clk_wzrd_reconfig = REG_FIELD(0x25C, 0, 1); * @max_parent:maximum parent clk rate * @int_field: pointer to regmap field for integer part * @frac_field:pointer to regmap field for fractional part + * @debug: debug information * * Flags: * WZRD_FLAG_MULTIPLY Clock is a multiplier rather than a divider @@ -334,6 +341,9 @@ struct clk_wzrd_clk_data { unsigned long max_parent; struct regmap_field *int_field; struct regmap_field *frac_field; +#ifdef CONFIG_DEBUG_FS + struct clk_wzrd_debug debug; +#endif }; #define to_clk_wzrd_clk_data(_hw) \ @@ -794,6 +804,39 @@ static int clk_wzrd_ratio_show(struct seq_file *s, void *data) } DEFINE_SHOW_ATTRIBUTE(clk_wzrd_ratio); +static int clk_wzrd_test_get_round_rate(void *data, u64 *val) +{ + struct clk_wzrd_clk_data *cwc = data; + + *val = cwc->debug.round_rate; + return 0; +} + +static int clk_wzrd_test_set_round_rate(void *data, u64 val) +{ + long round_rate; + struct clk_wzrd_clk_data *cwc = data; + + round_rate = clk_round_rate(cwc->hw.clk, (unsigned long)val); + + if (round_rate < 0) + return round_rate; + + cwc->debug.round_rate = round_rate; + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(fops_test_round_rate, clk_wzrd_test_get_round_rate, +clk_wzrd_test_set_round_rate, "%llu\n"); + +static int clk_wzrd_test_set_set_rate(void *data, u64 val) +{ + struct clk_wzrd_clk_data *cwc = data; + + return clk_set_rate(cwc->hw.clk, (unsigned long)val); +} +DEFINE_DEBUGFS_ATTRIBUTE(fops_test_set_rate, NULL, +clk_wzrd_test_set_set_rate, "%llu\n"); + static int clk_wzrd_debug_init(struct clk_hw *hw, struct dentry *dentry) { struct dentry *d; @@ -841,6 +884,16 @@ static int clk_wzrd_debug_init(struct clk_hw *hw, struct dentry *dentry) if (IS_ERR(d)) return PTR_ERR(d); + d = debugfs_create_file_unsafe("test_round_rate", 0644, dentry, cwc, + &fops_test_round_rate); + if (IS_ERR(d)) + return PTR_ERR(d); + + d = debugfs_create_file_unsafe("test_set_rate", 0200, dentry, cwc, + &fops_test_set_rate); + if (IS_ERR(d)) + return PTR_ERR(d); + return 0; } #endif -- 2.15.1 (Apple Git-101) ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH 10/14] staging: clocking-wizard: Support clk_round_rate
Add support for the clk_round_rate API to our CCF clock provider. Signed-off-by: James Kelly --- .../clocking-wizard/clk-xlnx-clock-wizard.c| 107 + 1 file changed, 107 insertions(+) diff --git a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c index 8929913045e7..8828dac6faaf 100644 --- a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c +++ b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c @@ -83,6 +83,7 @@ #define WZRD_CLKNAME_IN1 "clk_in1" #define WZRD_FLAG_MULTIPLY BIT(0) #define WZRD_FLAG_FRAC BIT(1) +#define WZRD_FLAG_ADJUST_MIN BIT(2) /* * Clock rate constraints extracted from Xilinx data sheets listed below. @@ -309,16 +310,19 @@ static const struct reg_field clk_wzrd_reconfig = REG_FIELD(0x25C, 0, 1); * * @hw:handle between common and hardware-specific interfaces * @flags: hardware specific flags + * @ratio_limit: pointer to divider/multiplier ratio limits * @int_field: pointer to regmap field for integer part * @frac_field:pointer to regmap field for fractional part * * Flags: * WZRD_FLAG_MULTIPLY Clock is a multiplier rather than a divider * WZRD_FLAG_FRAC Clock ratio can be fractional + * WZRD_FLAG_ADJUST_MINWhen clock is fractional minimum ratio increases by 1 */ struct clk_wzrd_clk_data { struct clk_hw hw; unsigned long flags; + const struct clk_wzrd_ratio *ratio_limit; struct regmap_field *int_field; struct regmap_field *frac_field; }; @@ -425,6 +429,86 @@ static unsigned long clk_wzrd_recalc_rate(struct clk_hw *hw, return clk_wzrd_calc_rate(parent_rate, ratio, cwc->flags); } +static unsigned long clk_wzrd_calc_ratio(unsigned long parent_rate, +unsigned long req_rate, +unsigned long flags) +{ + unsigned long long t; + unsigned long n, d; + + if (flags & WZRD_FLAG_MULTIPLY) { + n = req_rate; + d = parent_rate; + } else { + n = parent_rate; + d = req_rate; + } + + if (flags & WZRD_FLAG_FRAC) { + /* Round at least significant bit */ + t = (unsigned long long)n << WZRD_FRAC_BITS; + return (unsigned long)DIV_ROUND_CLOSEST_ULL(t, d); + } + + /* Round at decimal point */ + t = (unsigned long long)n; + return (unsigned long)DIV_ROUND_CLOSEST_ULL(t, d) << WZRD_FRAC_BITS; +} + +static unsigned long clk_wzrd_limit_calc_ratio(struct clk_wzrd_clk_data *cwc, + unsigned long parent_rate, + unsigned long req_rate) +{ + unsigned long ratio; + + ratio = clk_wzrd_calc_ratio(parent_rate, req_rate, cwc->flags); + + /* +* Some fractional multiple/divide hardware cannot do fractional +* multiply/divide between the minimum ratio limit and the +* minimum ratio limit + 1, as indicated by WZRD_FLAG_ADJUST_MIN. +* If we get a ratio in this range we have to recalculate the +* ratio so it is not fractional and rounded to an integer. +*/ + if (ratio >> WZRD_FRAC_BITS == cwc->ratio_limit->min && + cwc->flags & WZRD_FLAG_ADJUST_MIN && cwc->flags & WZRD_FLAG_FRAC) + ratio = clk_wzrd_calc_ratio(parent_rate, req_rate, + cwc->flags & ~WZRD_FLAG_FRAC); + + ratio = clamp_val(ratio, cwc->ratio_limit->min << WZRD_FRAC_BITS, + cwc->ratio_limit->max << WZRD_FRAC_BITS); + + return ratio; +} + +static inline unsigned long clk_wzrd_round_rate(struct clk_wzrd_clk_data *cwc, + unsigned long parent_rate, + unsigned long req_rate) +{ + unsigned long ratio = clk_wzrd_limit_calc_ratio(cwc, parent_rate, + req_rate); + return clk_wzrd_calc_rate(parent_rate, ratio, cwc->flags); +} + +static int clk_wzrd_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_wzrd_clk_data *cwc = to_clk_wzrd_clk_data(hw); + + if (cwc->flags & WZRD_FLAG_MULTIPLY && req->best_parent_rate == 0) + return -EINVAL; + + if (!(cwc->flags & WZRD_FLAG_MULTIPLY) && req->rate == 0) + return -EINVAL; + + req->rate = clk_wzrd_round_rate(cwc, req->best_parent_rate, req->rate); + + if (req->rate
[PATCH 12/14] staging: clocking-wizard: Automatically set internal clock rates
Allow CLK_SET_RATE_PARENT to be optionally enabled on one of the output clocks. This will automatically choose the "best" rates for the first divider and PLL multiplier. Best is defined as those first divider and PLL multplier rates that minimise the error in the rate of the output clock that has CLK_SET_RATE_PARENT enabled. The current implementation uses a constrained brute force search for the best parent rate that stops when a rate is within 10ppm of that requested. The output clock for which CLK_SET_RATE_PARENT should be enabled is specified using a new device-tree property named "set-parent-output". This is an optional property and if not present this feature is disabled. If first divider clock is updated before the PLL multiplier clock adjust the PLL multiplier to keep PLL lock. Signed-off-by: James Kelly --- drivers/staging/clocking-wizard/TODO | 1 - .../clocking-wizard/clk-xlnx-clock-wizard.c| 151 - drivers/staging/clocking-wizard/dt-binding.txt | 3 + 3 files changed, 149 insertions(+), 6 deletions(-) diff --git a/drivers/staging/clocking-wizard/TODO b/drivers/staging/clocking-wizard/TODO index 50193bdd61e1..bf7435c5b67e 100644 --- a/drivers/staging/clocking-wizard/TODO +++ b/drivers/staging/clocking-wizard/TODO @@ -1,7 +1,6 @@ TODO: - review arithmetic - overflow after multiplication? - - implement CLK_SET_RATE_PARENT to set internal clocks - implement CLK_SET_RATE_PARENT to set input clock - test on 64-bit ARM and Microblaze architectures. - support clk_set_phase diff --git a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c index 455ee9887c77..f706c3d6496e 100644 --- a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c +++ b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c @@ -315,6 +315,8 @@ static const struct reg_field clk_wzrd_reconfig = REG_FIELD(0x25C, 0, 1); * @flags: hardware specific flags * @cw;pointer to platform device data * @ratio_limit: pointer to divider/multiplier ratio limits + * @min_parent:minimum parent clk rate + * @max_parent:maximum parent clk rate * @int_field: pointer to regmap field for integer part * @frac_field:pointer to regmap field for fractional part * @@ -328,6 +330,8 @@ struct clk_wzrd_clk_data { unsigned long flags; struct clk_wzrd *cw; const struct clk_wzrd_ratio *ratio_limit; + unsigned long min_parent; + unsigned long max_parent; struct regmap_field *int_field; struct regmap_field *frac_field; }; @@ -501,10 +505,77 @@ static inline unsigned long clk_wzrd_round_rate(struct clk_wzrd_clk_data *cwc, return clk_wzrd_calc_rate(parent_rate, ratio, cwc->flags); } +static inline unsigned long clk_wzrd_parent_rate(struct clk_wzrd_clk_data *cwc, +unsigned long rate, +unsigned long ratio) +{ + return clk_wzrd_calc_rate(rate, ratio, cwc->flags ^ + WZRD_FLAG_MULTIPLY); +} + +/* + * Search for the best parent rate + * + * This has the potential to run a long time if our parent clocks also + * search for their best parent rate. + */ +static bool clk_wzrd_best_parent_rate(struct clk_wzrd_clk_data *cwc, + struct clk_rate_request *req) +{ + unsigned long ratio, min_ratio, max_ratio; + unsigned long min_parent_rate = cwc->min_parent; + unsigned long max_parent_rate = cwc->max_parent; + unsigned long best_delta = ULONG_MAX; + unsigned long inc = cwc->flags & WZRD_FLAG_FRAC ? 1 : + BIT(WZRD_FRAC_BITS); + + /* +* Search by testing parent rates that corresponds to all possible +* ratios that are within our parent rate constraints. +*/ + min_ratio = clk_wzrd_limit_calc_ratio(cwc, min_parent_rate, req->rate); + max_ratio = clk_wzrd_limit_calc_ratio(cwc, max_parent_rate, req->rate); + + if (cwc->flags & WZRD_FLAG_MULTIPLY) + swap(min_ratio, max_ratio); + + for (ratio = max_ratio; ratio >= min_ratio; ratio -= inc) { + unsigned long parent_rate, rate, delta; + + parent_rate = clk_wzrd_parent_rate(cwc, req->rate, ratio); + + /* +* Figure out what rate we will actually end up with. This +* may take a while if our parent can set their parent rate. +*/ + parent_rate = clk_hw_round_rate(req->best_parent_hw, + pa
[PATCH 09/14] staging: clocking-wizard: Provide more information in debugfs
Publish clock divider/multiplier ratios and flags specific to our clock provider implementation as these are not available via the debugfs entries provided by the common clock framework. Signed-off-by: James Kelly --- .../clocking-wizard/clk-xlnx-clock-wizard.c| 57 ++ 1 file changed, 57 insertions(+) diff --git a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c index c892c0d46801..8929913045e7 100644 --- a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c +++ b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c @@ -69,6 +69,8 @@ #include #include #include +#include +#include #define WZRD_MAX_OUTPUTS 7 #define KHz1000UL @@ -423,8 +425,63 @@ static unsigned long clk_wzrd_recalc_rate(struct clk_hw *hw, return clk_wzrd_calc_rate(parent_rate, ratio, cwc->flags); } +#ifdef CONFIG_DEBUG_FS + +static int clk_wzrd_flags_show(struct seq_file *s, void *data) +{ + struct clk_wzrd_clk_data *cwc = s->private; + + seq_puts(s, "Flags:\n"); + if (cwc->flags & WZRD_FLAG_MULTIPLY) + seq_puts(s, "WZRD_FLAG_MULTIPLY\n"); + if (cwc->flags & WZRD_FLAG_FRAC) + seq_puts(s, "WZRD_FLAG_FRAC\n"); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(clk_wzrd_flags); + +static int clk_wzrd_ratio_show(struct seq_file *s, void *data) +{ + struct clk_wzrd_clk_data *cwc = s->private; + unsigned int int_part, frac_part = 0; + + regmap_field_read(cwc->int_field, &int_part); + if (cwc->flags & WZRD_FLAG_FRAC) { + regmap_field_read(cwc->frac_field, &frac_part); + seq_printf(s, "%u.%u\n", int_part, frac_part); + } else { + seq_printf(s, "%u\n", int_part); + } + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(clk_wzrd_ratio); + +static int clk_wzrd_debug_init(struct clk_hw *hw, struct dentry *dentry) +{ + struct dentry *d; + struct clk_wzrd_clk_data *cwc = to_clk_wzrd_clk_data(hw); + + d = debugfs_create_file_unsafe("clk_hw_flags", 0444, dentry, cwc, + &clk_wzrd_flags_fops); + if (IS_ERR(d)) + return PTR_ERR(d); + + d = debugfs_create_file_unsafe("clk_ratio", 0444, dentry, cwc, + &clk_wzrd_ratio_fops); + if (IS_ERR(d)) + return PTR_ERR(d); + + return 0; +} +#endif + static const struct clk_ops clk_wzrd_clk_ops = { .recalc_rate = clk_wzrd_recalc_rate, +#ifdef CONFIG_DEBUG_FS + .debug_init = clk_wzrd_debug_init, +#endif }; static int clk_wzrd_register_clk(struct device *dev, const char *name, -- 2.15.1 (Apple Git-101) ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
[PATCH 11/14] staging: clocking-wizard: Support clk_set_rate
Provide initial support for CCF clk_set_rate API on all clock components. Clock consumers that want to set the first divider or PLL clock will need to use clk_get_parent on one of the output clocks as there is no support for CLK_SET_RATE_PARENT yet. Care must be taken when setting the first divider clock to ensure that the PLL clock rate will remain within a valid range for the VCO, as it is impossible to subsequently update any clock if the PLL does not lock. A subsequent patch will address this issue. Signed-off-by: James Kelly --- drivers/staging/clocking-wizard/TODO | 4 +- .../clocking-wizard/clk-xlnx-clock-wizard.c| 115 + 2 files changed, 117 insertions(+), 2 deletions(-) diff --git a/drivers/staging/clocking-wizard/TODO b/drivers/staging/clocking-wizard/TODO index 53c9941fcc35..50193bdd61e1 100644 --- a/drivers/staging/clocking-wizard/TODO +++ b/drivers/staging/clocking-wizard/TODO @@ -1,8 +1,8 @@ TODO: - - support for set_rate() operations (may benefit from Stephen Boyd's - refactoring of the clk primitives: https://lkml.org/lkml/2014/9/5/766) - review arithmetic - overflow after multiplication? + - implement CLK_SET_RATE_PARENT to set internal clocks + - implement CLK_SET_RATE_PARENT to set input clock - test on 64-bit ARM and Microblaze architectures. - support clk_set_phase diff --git a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c index 8828dac6faaf..455ee9887c77 100644 --- a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c +++ b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c @@ -76,6 +76,7 @@ #define KHz1000UL #define MHz100UL #define WZRD_ACLK_MAX_FREQ (250 * MHz) +#define WZRD_PLL_LOCK_TIMEOUT 1000// usec #define WZRD_FRAC_BITS 3 #define WZRD_FRAC_MASK (BIT(WZRD_FRAC_BITS) - 1) #define WZRD_FRAC_SCALE(1000 >> WZRD_FRAC_BITS) @@ -85,6 +86,8 @@ #define WZRD_FLAG_FRAC BIT(1) #define WZRD_FLAG_ADJUST_MIN BIT(2) +struct clk_wzrd; + /* * Clock rate constraints extracted from Xilinx data sheets listed below. * The minimum rates depend on family and clock type and the maximum rates @@ -310,6 +313,7 @@ static const struct reg_field clk_wzrd_reconfig = REG_FIELD(0x25C, 0, 1); * * @hw:handle between common and hardware-specific interfaces * @flags: hardware specific flags + * @cw;pointer to platform device data * @ratio_limit: pointer to divider/multiplier ratio limits * @int_field: pointer to regmap field for integer part * @frac_field:pointer to regmap field for fractional part @@ -322,6 +326,7 @@ static const struct reg_field clk_wzrd_reconfig = REG_FIELD(0x25C, 0, 1); struct clk_wzrd_clk_data { struct clk_hw hw; unsigned long flags; + struct clk_wzrd *cw; const struct clk_wzrd_ratio *ratio_limit; struct regmap_field *int_field; struct regmap_field *frac_field; @@ -344,6 +349,9 @@ struct clk_wzrd_clk_data { * @div: Divider internal clock provider data * @pll: Phase locked loop internal clock provider data * @chip: Chip data including rate constraints + * @pll_locked:Phase locked loop locked status regmap field + * @reconfig: Reconfiguration regmap field + * @dev: Handle to device * @clkout_data: Array of output clock provider data */ struct clk_wzrd { @@ -358,6 +366,9 @@ struct clk_wzrd { struct clk_wzrd_clk_datadiv_data; struct clk_wzrd_clk_datapll_data; const struct clk_wzrd_chip *chip; + struct regmap_field *pll_locked; + struct regmap_field *reconfig; + struct device *dev; struct clk_wzrd_clk_dataclkout_data[0]; }; #define to_clk_wzrd(_nb) container_of(_nb, struct clk_wzrd, nb) @@ -509,6 +520,97 @@ static int clk_wzrd_determine_rate(struct clk_hw *hw, return 0; } +static bool clk_wzrd_set_ratio(struct clk_wzrd_clk_data *cwc, + unsigned long parent_rate, + unsigned long rate) +{ + unsigned long old_ratio = clk_wzrd_get_ratio(cwc); + unsigned long ratio = clk_wzrd_limit_calc_ratio(cwc, parent_rate, rate); + + if (ratio == old_ratio) + return false; + + regmap_field_write(cwc->int_field, ratio >> WZRD_FRAC_BITS); + if (cwc->flags & WZRD_FLAG_FRAC) + regmap_field_write(cwc->frac_field, (ratio & WZRD_FRAC_MASK) * + WZRD_FRAC_SCALE); + +
[PATCH 13/14] staging: clocking-wizard: Automatically set input clock rate
Allow CLK_SET_RATE_PARENT to be optionally enabled on first divider clock. This has the potential to set the rate of one output clock with more precision. On Zynq-7000 this is typically achieved using a PS FCLK as input to the Clocking Wizard IP. This feature is enabled by the optional device-tree property "set-input-rate-range". The presence of this property in the device tree enables the feature. This feature is only active if the "set-parent-output" device-tree property is also present as it depends on the feature enabled by that property. The value of the "set-input-rate-range" allows the input rate to be constrained to a narrower range than the hardware supports as this is useful in some circumstances. The input rate is then further constrained to ensure the PLL can always lock when the input rate is changed. Signed-off-by: James Kelly --- drivers/staging/clocking-wizard/TODO | 1 - .../clocking-wizard/clk-xlnx-clock-wizard.c| 89 +- drivers/staging/clocking-wizard/dt-binding.txt | 4 + 3 files changed, 89 insertions(+), 5 deletions(-) diff --git a/drivers/staging/clocking-wizard/TODO b/drivers/staging/clocking-wizard/TODO index bf7435c5b67e..2a563ca67cd2 100644 --- a/drivers/staging/clocking-wizard/TODO +++ b/drivers/staging/clocking-wizard/TODO @@ -1,7 +1,6 @@ TODO: - review arithmetic - overflow after multiplication? - - implement CLK_SET_RATE_PARENT to set input clock - test on 64-bit ARM and Microblaze architectures. - support clk_set_phase diff --git a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c index f706c3d6496e..bb64da849d9b 100644 --- a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c +++ b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c @@ -356,6 +356,8 @@ struct clk_wzrd_clk_data { * @pll_locked:Phase locked loop locked status regmap field * @reconfig: Reconfiguration regmap field * @dev: Handle to device + * @min_input: Current minimum input rate to ensure PLL lock + * @max_input: Current maximum input rate to ensure PLL lock * @clkout_data: Array of output clock provider data */ struct clk_wzrd { @@ -373,6 +375,8 @@ struct clk_wzrd { struct regmap_field *pll_locked; struct regmap_field *reconfig; struct device *dev; + unsigned long min_input; + unsigned long max_input; struct clk_wzrd_clk_dataclkout_data[0]; }; #define to_clk_wzrd(_nb) container_of(_nb, struct clk_wzrd, nb) @@ -529,6 +533,15 @@ static bool clk_wzrd_best_parent_rate(struct clk_wzrd_clk_data *cwc, unsigned long inc = cwc->flags & WZRD_FLAG_FRAC ? 1 : BIT(WZRD_FRAC_BITS); + /* +* Apply extra constraint to ensure PLL lock when looking for the +* best rate at the input of the Clocking Wizard IP. +*/ + if (cwc == &cwc->cw->div_data) { + min_parent_rate = cwc->cw->min_input; + max_parent_rate = cwc->cw->max_input; + } + /* * Search by testing parent rates that corresponds to all possible * ratios that are within our parent rate constraints. @@ -594,6 +607,30 @@ static int clk_wzrd_determine_rate(struct clk_hw *hw, return 0; } +static void clk_wzrd_update_input_constraint(struct clk_wzrd *cw) +{ + unsigned long min_pfd, max_pfd; + struct clk_wzrd_clk_data *out_cwc = &cw->clkout_data[0]; + struct clk_wzrd_clk_data *pll_cwc = &cw->pll_data; + struct clk_wzrd_clk_data *div_cwc = &cw->div_data; + unsigned long current_pll_ratio = clk_wzrd_get_ratio(pll_cwc); + unsigned long current_div_ratio = clk_wzrd_get_ratio(div_cwc); + + min_pfd = max(clk_wzrd_parent_rate(pll_cwc, out_cwc->min_parent, + current_pll_ratio), + pll_cwc->min_parent); + max_pfd = min(clk_wzrd_parent_rate(pll_cwc, out_cwc->max_parent, + current_pll_ratio), + pll_cwc->max_parent); + + cw->min_input = max(clk_wzrd_parent_rate(div_cwc, min_pfd, +current_div_ratio), + div_cwc->min_parent); + cw->max_input = min(clk_wzrd_parent_rate(div_cwc, max_pfd, +current_div_ratio), + div_cwc->max_parent); +} + static bool clk_wzrd_set_ratio(struct clk_wzrd_clk_data *cwc, unsigned long parent_rate, unsigned long rate) @@ -716,6 +753,9 @@ static int clk_wzrd_set_rate(stru
[PATCH 07/14] staging: clocking-wizard: Add hardware constaints
Existing constraints in the driver were overly simplistic and do not accurately represent the diversity of Clocking Wizard IP implementations. Constant data added to express the constraints around available rates, divider/multiplier ratios and hardware features. These were extracted from the relevant Xilinx documentation. Further details on the sources used for these constraints can be found in the driver comments. Two device tree properties were added to choose the correct constraints for available rates, divider/multiplier ratios and hardware features. The default for the new "xlnx,family" property is Kintex-7 and the default for the new "xlnx,primitive" property is MMCM. These defaults have been chosen to match the current driver behaviour which uses the Kintex-7 rates for speed-grade and a number of clock outputs that corresonds to an MMCM. Speed grade now defaults to the least restrictive (fastest) speed grade. Speed grade is now represented within the driver as an enum to enable possible future support of non-integer speed grades, however it has not been implemented here to avoid breaking the existing device tree bindings. Signed-off-by: James Kelly --- .../clocking-wizard/clk-xlnx-clock-wizard.c| 307 ++--- drivers/staging/clocking-wizard/dt-binding.txt | 12 +- 2 files changed, 276 insertions(+), 43 deletions(-) diff --git a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c index 1dbeda92fb9a..3e670cdc072c 100644 --- a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c +++ b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c @@ -70,12 +70,179 @@ #include #include -#define WZRD_NUM_OUTPUTS 7 -#define WZRD_ACLK_MAX_FREQ 25000UL +#define WZRD_MAX_OUTPUTS 7 +#define KHz1000UL +#define MHz100UL +#define WZRD_ACLK_MAX_FREQ (250 * MHz) #define WZRD_CLKNAME_AXI "s_axi_aclk" #define WZRD_CLKNAME_IN1 "clk_in1" #define WZRD_FLAG_MULTIPLY BIT(0) +/* + * Clock rate constraints extracted from Xilinx data sheets listed below. + * The minimum rates depend on family and clock type and the maximum rates + * also depend on speed grade and supply voltage. + * + * At this time the driver does not support the low voltage speed grades (-xL) + * and it does not model the impact of supply voltage. + * + * The constraints are enforced by the common clock framework when a client + * issues a request to set the rate of a clock. + * + * DS181 Artix-7 FPGAs Data Sheet: DC and AC Switching Characteristics + * DS182 Kintex-7 FPGAs Data Sheet: DC and AC Switching Characteristics + * DS183 Virtex-7 T and XT FPGAs Data Sheet: DC and AC Switching Characteristics + * DS187 Zynq-7000 All Programmable SoC + * (Z-7007S, Z-7012S, Z-7014S, Z-7010, Z-7015, and Z-7020): + * DC and AC Switching Characteristics + * DS191 Zynq-7000 All Programmable SoC + * (Z-7030, Z-7035, Z-7045, and Z-7100): + * DC and AC Switching Characteristics + * DS892 Kintex UltraScale FPGAs Data Sheet: DC and AC Switching Characteristics + * DS893 Virtex UltraScale FPGAs Data Sheet: DC and AC Switching Characteristics + * DS922 Kintex UltraScale+ FPGAs Data Sheet: + * DC and AC Switching Characteristics + * DS923 Virtex UltraScale+ FPGA Data Sheet: DC and AC Switching Characteristics + * DS925 Zynq UltraScale+ MPSoC Data Sheet: DC and AC Switching Characteristics + */ + +enum clk_wzrd_rate { + WZRD_RATE_FIN, // Clock wizard input frequency + WZRD_RATE_PFD, // Input to phase frequency detector + WZRD_RATE_VCO, // Output of voltage controlled oscillator + WZRD_RATE_OUT, // Clock wizard output frequency + WZRD_RATE_COUNT +}; + +enum clk_wzrd_speed { + WZRD_SPEED_1, // Speed grade 1 + WZRD_SPEED_2, // Speed grade 2 + WZRD_SPEED_3, // Speed grade 3 + WZRD_SPEED_COUNT +}; + +enum clk_wzrd_family { + WZRD_FAMILY_ARTIX7, // Includes low-end Zynq-7000 + WZRD_FAMILY_KINTEX7,// Includes Vertex-7, high-end Zynq-7000 + WZRD_FAMILY_ULTRASCALE, // Kintex and Vertex Ultrascale + WZRD_FAMILY_ULTRASCALEPLUS, // Kintex, Vertex, Zynq Ultrascale+ + WZRD_FAMILY_COUNT +}; + +static const char *clk_wzrd_family_name[WZRD_FAMILY_COUNT] = { + "Artix-7", + "Kintex-7", + "Ultrascale", + "Ultrascale+" +}; + +enum clk_wzrd_type { + WZRD_TYPE_MMCM, // Mixed Mode Clock Manager + WZRD_TYPE_PLL, // Phased Lock Loop + WZRD_TYPE_COUNT +}; + +static const char *clk_wzrd_type_name[WZRD_TYPE_COUNT] = { + "MMCM", + "PLL" +}; + +enum clk_wzrd_cell { + WZRD_CELL_MMCME2, // 7 series MMCM + WZRD_CELL_PLLE
[PATCH 08/14] staging: clocking-wizard: Support fractional ratios
Update clock provider to support fraction divider and multiplier ratios. Ratios are now fixed point unsigned numbers with the lower WZRD_FRAC_BITS containing the fractional part of the ratio. Fractional ratios are only supported on MMCM primitives and only for the PLL multiplier and first output divider. Use DIV_ROUND_CLOSEST_ULL for division to provide best precision. Signed-off-by: James Kelly --- drivers/staging/clocking-wizard/TODO | 5 +- .../clocking-wizard/clk-xlnx-clock-wizard.c| 68 -- 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/drivers/staging/clocking-wizard/TODO b/drivers/staging/clocking-wizard/TODO index ebe99db7d153..53c9941fcc35 100644 --- a/drivers/staging/clocking-wizard/TODO +++ b/drivers/staging/clocking-wizard/TODO @@ -1,11 +1,10 @@ TODO: - - support for fractional multiplier - - support for fractional divider (output 0 only) - support for set_rate() operations (may benefit from Stephen Boyd's refactoring of the clk primitives: https://lkml.org/lkml/2014/9/5/766) - review arithmetic - overflow after multiplication? - - maximize accuracy before divisions + - test on 64-bit ARM and Microblaze architectures. + - support clk_set_phase Patches to: Greg Kroah-Hartman diff --git a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c index 3e670cdc072c..c892c0d46801 100644 --- a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c +++ b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c @@ -74,9 +74,13 @@ #define KHz1000UL #define MHz100UL #define WZRD_ACLK_MAX_FREQ (250 * MHz) +#define WZRD_FRAC_BITS 3 +#define WZRD_FRAC_MASK (BIT(WZRD_FRAC_BITS) - 1) +#define WZRD_FRAC_SCALE(1000 >> WZRD_FRAC_BITS) #define WZRD_CLKNAME_AXI "s_axi_aclk" #define WZRD_CLKNAME_IN1 "clk_in1" #define WZRD_FLAG_MULTIPLY BIT(0) +#define WZRD_FLAG_FRAC BIT(1) /* * Clock rate constraints extracted from Xilinx data sheets listed below. @@ -304,14 +308,17 @@ static const struct reg_field clk_wzrd_reconfig = REG_FIELD(0x25C, 0, 1); * @hw:handle between common and hardware-specific interfaces * @flags: hardware specific flags * @int_field: pointer to regmap field for integer part + * @frac_field:pointer to regmap field for fractional part * * Flags: * WZRD_FLAG_MULTIPLY Clock is a multiplier rather than a divider + * WZRD_FLAG_FRAC Clock ratio can be fractional */ struct clk_wzrd_clk_data { struct clk_hw hw; unsigned long flags; struct regmap_field *int_field; + struct regmap_field *frac_field; }; #define to_clk_wzrd_clk_data(_hw) \ @@ -349,23 +356,56 @@ struct clk_wzrd { }; #define to_clk_wzrd(_nb) container_of(_nb, struct clk_wzrd, nb) +/* + * The following functions use or generate fixed point ratios + * + * The lower WZRD_FRAC_BITS of fixed point ratios are the fractional + * part and the remaining bits are the integer part. + * + * When doing division we make sure to round once only and at + * ether the decimal point or the end of the fixed point number + * depending on whether the hardware can multiply/divide by only + * integer ratios, or can multiply/divide by fixed point ratios. + */ + static unsigned long clk_wzrd_get_ratio(struct clk_wzrd_clk_data *cwc) { u32 int_part; + unsigned long ratio; regmap_field_read(cwc->int_field, &int_part); + ratio = int_part << WZRD_FRAC_BITS; + + if (cwc->flags & WZRD_FLAG_FRAC) { + u32 frac_part; - return int_part; + regmap_field_read(cwc->frac_field, &frac_part); + ratio |= frac_part / WZRD_FRAC_SCALE; + } + + return ratio; } static unsigned long clk_wzrd_calc_rate(unsigned long parent_rate, unsigned long ratio, unsigned long flags) { - if (flags & WZRD_FLAG_MULTIPLY) - return parent_rate * ratio; + unsigned long long t; - return DIV_ROUND_CLOSEST(parent_rate, ratio); + if (flags & WZRD_FLAG_MULTIPLY) { + t = (unsigned long long)parent_rate * ratio; + return (unsigned long)DIV_ROUND_CLOSEST_ULL(t, BIT(WZRD_FRAC_BITS)); + } + + if (flags & WZRD_FLAG_FRAC) { + /* Round at least significant bit */ + t = (unsigned long long)parent_rate << WZRD_FRAC_BITS; + return (unsigned long)DIV_ROUND_CLOSEST_ULL(t, ratio); + } + + /* Round at decimal point */ + t = (unsigned long lo
Re: [PATCH 03/14] staging: clocking-wizard: Split probe function
Dan, > On 14 May 2018, at 11:47 pm, Dan Carpenter wrote: > > On Mon, May 07, 2018 at 11:20:29AM +1000, James Kelly wrote: >> +static int clk_wzrd_probe(struct platform_device *pdev) >> +{ >> +int ret; >> +struct device *dev = &pdev->dev; >> + >> +ret = clk_wzrd_get_device_tree_data(dev); >> +if (ret) >> +return ret; >> + >> +ret = clk_wzrd_regmap_alloc(dev); >> +if (ret) >> +return ret; >> + >> +ret = clk_wzrd_register_ccf(dev); >> +if (ret) >> +return ret; >> + >> +return 0; > > The error handling is a terrible layer violation now... Every > allocation function should have a matching free function. But now > instead of that if clk_wzrd_register_ccf() fails then it starts cleaning > up the clk_wzrd->axi_clk that was allocated in > clk_wzrd_get_device_tree_data(). > > regards, > dan carpenter > > This gets cleaned up in patch 6. There seemed little point in putting a whole lot of code in patch 3 only to rip it out again in patch 6. I was trying to keep the patches simple. I could defer breaking up the probe function until after the changes in patch 6 ? James ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel
Re: [PATCH 00/14] staging: clocking-wizard: Implement many TODOs
Greg, > On 14 May 2018, at 10:02 pm, Greg Kroah-Hartman > wrote: > > Can you please fix up the issues reported in this series, rebase on my > latest tree, and resend so that I can apply them? > > thanks, > > greg k-h I’m working on a version 2 patch set and will post when it is ready. James ___ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel