On Tue, Mar 22, 2016 at 08:57:13AM +0200, Tero Kristo wrote: > On 03/22/2016 03:22 AM, Ladislav Michl wrote: > >Something like patch v0 bellow. However note, that rate is determined using > >determine_rate, therefore omap3630_noncore_dpll_determine_rate which is just > >a copy of omap2_noncore_dpll_determine_rate is calling > >omap3630_dpll_round_rate - shame, shall we use function pointer > >determine_rate? Neither is nice and elegant. Also we somehow need to know > >soc_is_omap3630() and bringing is to drivers/clk does not seem as an option. > >Also note, that there's no choice between options for 26MHz. Hints? > > What do you mean, no choice between options for 26MHz? Table 36 list two options with 26MHz clocks: m=443, n=11 and m=480, n=12 with a statement: "The choice between these two options with a 26 MHz input should be based on characterization on the end system." [... patch updated] > >--- drivers/clk/ti/clkt_dpll.c.orig 2016-03-22 01:59:21.724896607 +0100 > >+++ drivers/clk/ti/clkt_dpll.c 2016-03-22 01:53:41.072896607 +0100 > >@@ -368,3 +368,46 @@ > > > > return dd->last_rounded_rate; > > } > >+ > >+/** > >+ * omap3630_dpll_round_rate - round a target rate for an OMAP DPLL > >+ * @clk: struct clk * for a DPLL > >+ * @target_rate: desired DPLL clock rate > >+ * > >+ * DM3730 errata (sprz319e), advisory 2.1, table 36 implementation > >+ */ > >+long omap3630_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate, > >+ unsigned long *parent_rate) > >+{ > >+ unsigned long rate; > >+ struct dpll_data *dd; > >+ struct clk_hw_omap *clk = to_clk_hw_omap(hw); > >+ > >+ if (!clk || !clk->dpll_data) > >+ return ~0; > > I don't think you need these checks here, this condition can never happen. I just did it consistently with other clock functions. Then those checks all around should be removed instead. Also see bellow (*) [... patch updated] > >+int omap3630_noncore_dpll_determine_rate(struct clk_hw *hw, > >+ struct clk_rate_request *req) > >+{ > >+ struct clk_hw_omap *clk = to_clk_hw_omap(hw); > >+ struct dpll_data *dd; > >+ > >+ if (!req->rate) > >+ return -EINVAL; > >+ > >+ dd = clk->dpll_data; > >+ if (!dd) > >+ return -EINVAL; > >+ > >+ if (clk_get_rate(dd->clk_bypass) == req->rate && > >+ (dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) { > >+ req->best_parent_hw = __clk_get_hw(dd->clk_bypass); > >+ } else { > >+ req->rate = omap3630_dpll_round_rate(hw, req->rate, > >+ &req->best_parent_rate); > >+ req->best_parent_hw = __clk_get_hw(dd->clk_ref); > >+ } > >+ > >+ req->best_parent_rate = req->rate; > >+ > >+ return 0; > > How about something like: > > int omap3630_noncore_dpll_determine_rate(struct clk_hw *hw, > struct clk_rate_request *req) > { > struct clk_hw_omap *clk = to_clk_hw_omap(hw); > struct dpll_data *dd; > > dd = clk->dpll_data; (*) why it is here then? > if (!dd) > return -EINVAL; > > if (req->rate == 120000000) { > req->rate = omap3630_dpll_round_rate(hw, req->rate, > &req->best_parent_rate); > req->best_parent_hw = __clk_get_hw(dd->clk_ref); > > return 0; > } > > return omap3_noncore_dpll_determine_rate(hw, req); > } > > You can drop the checks against 120MHz inside round_rate then. What if omap3630_dpll_round_rate is called directly as ops->round_rate()? best regards, ladis --- drivers/clk/ti/dpll3xxx.c.orig 2016-03-21 22:55:29.515746383 +0100 +++ drivers/clk/ti/dpll3xxx.c 2016-03-22 01:43:54.004896607 +0100 @@ -534,6 +534,33 @@ return 0; } +int omap3630_noncore_dpll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_hw_omap *clk = to_clk_hw_omap(hw); + struct dpll_data *dd; + + if (!req->rate) + return -EINVAL; + + dd = clk->dpll_data; + if (!dd) + return -EINVAL; + + if (clk_get_rate(dd->clk_bypass) == req->rate && + (dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) { + req->best_parent_hw = __clk_get_hw(dd->clk_bypass); + } else { + req->rate = omap3630_dpll_round_rate(hw, req->rate, + &req->best_parent_rate); + req->best_parent_hw = __clk_get_hw(dd->clk_ref); + } + + req->best_parent_rate = req->rate; + + return 0; +} + /** * omap3_noncore_dpll_set_parent - set parent for a DPLL clock * @hw: pointer to the clock to set parent for --- drivers/clk/ti/clkt_dpll.c.orig 2016-03-22 01:59:21.724896607 +0100 +++ drivers/clk/ti/clkt_dpll.c 2016-03-22 16:23:22.804383248 +0100 @@ -368,3 +368,52 @@ return dd->last_rounded_rate; } + +/** + * omap3630_dpll_round_rate - round a target rate for an OMAP DPLL + * @clk: struct clk * for a DPLL + * @target_rate: desired DPLL clock rate + * + * DM3730 errata (sprz319e), advisory 2.1 + */ +long omap3630_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate, + unsigned long *parent_rate) +{ + unsigned int i, m, n; + unsigned long r; + struct dpll_data *dd; + + static const struct { + unsigned long rate; + unsigned int m; + unsigned int n; /* value of the register is n - 1 */ + } dpll5_advisory[] = { + { 12000000, 80, 0 + 1, }, + { 19200000, 50, 0 + 1, }, + { 38400000, 25, 0 + 1, }, + { 13000000, 443, 5 + 1, }, + { 26000000, 443, 11 + 1, }, + { 26000000, 480, 12 + 1, }, + }; + + if (target_rate == 120000000) + for (i = 0; i < ARRAY_SIZE(dpll5_advisory); i++) + if (*parent_rate == dpll5_advisory[i].rate) { + m = dpll5_advisory[i].m; + n = dpll5_advisory[i].n; + r = _dpll_compute_new_rate(*parent_rate, m, n); + r /= 8; + + printk("clock %s: m=%d, n=%d, rate=%lu\n", + clk_hw_get_name(hw), m, n, r); + + dd = to_clk_hw_omap(hw)->dpll_data; + dd->last_rounded_m = m; + dd->last_rounded_n = n; + dd->last_rounded_rate = r; + + return r; + } + + return omap2_dpll_round_rate(hw, target_rate, parent_rate); +} --- drivers/clk/ti/dpll.c.orig 2016-03-21 22:53:16.379746383 +0100 +++ drivers/clk/ti/dpll.c 2016-03-22 16:27:08.284383248 +0100 @@ -114,6 +114,18 @@ .round_rate = &omap2_dpll_round_rate, }; +static const struct clk_ops omap3630_dpll_ck_ops = { + .enable = &omap3_noncore_dpll_enable, + .disable = &omap3_noncore_dpll_disable, + .get_parent = &omap2_init_dpll_parent, + .recalc_rate = &omap3_dpll_recalc, + .set_rate = &omap3_noncore_dpll_set_rate, + .set_parent = &omap3_noncore_dpll_set_parent, + .set_rate_and_parent = &omap3_noncore_dpll_set_rate_and_parent, + .determine_rate = &omap3630_noncore_dpll_determine_rate, + .round_rate = &omap3630_dpll_round_rate, +}; + static const struct clk_ops omap3_dpll_per_ck_ops = { .enable = &omap3_noncore_dpll_enable, .disable = &omap3_noncore_dpll_disable, @@ -466,6 +478,26 @@ CLK_OF_DECLARE(ti_omap3_dpll_clock, "ti,omap3-dpll-clock", of_ti_omap3_dpll_setup); +static void __init of_ti_omap3630_dpll5_setup(struct device_node *node) +{ + const struct dpll_data dd = { + .idlest_mask = 0x1, + .enable_mask = 0x7, + .autoidle_mask = 0x7, + .mult_mask = 0x7ff << 8, + .div1_mask = 0x7f, + .max_multiplier = 2047, + .max_divider = 128, + .min_divider = 1, + .freqsel_mask = 0xf0, + .modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED), + }; + + of_ti_dpll_setup(node, &omap3630_dpll_ck_ops, &dd); +} +CLK_OF_DECLARE(ti_omap3630_dpll5_clock, "ti,omap3630-dpll5-clock", + of_ti_omap3630_dpll5_setup); + static void __init of_ti_omap3_core_dpll_setup(struct device_node *node) { const struct dpll_data dd = { --- drivers/clk/ti/clock.h.orig 2016-03-21 22:55:09.011746383 +0100 +++ drivers/clk/ti/clock.h 2016-03-22 01:42:45.840896607 +0100 @@ -252,8 +252,12 @@ u8 index); int omap3_noncore_dpll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req); +int omap3630_noncore_dpll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req); long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate, unsigned long *parent_rate); +long omap3630_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate, + unsigned long *parent_rate); unsigned long omap3_clkoutx2_recalc(struct clk_hw *hw, unsigned long parent_rate); -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html