diff mbox

DM3730 sprz319 erratum 2.1

Message ID 20160322012215.GA6963@localhost.localdomain (mailing list archive)
State New, archived
Headers show

Commit Message

Ladislav Michl March 22, 2016, 1:22 a.m. UTC
On Thu, Mar 17, 2016 at 07:42:38AM -0700, Tony Lindgren wrote:
> * Richard Watts <rrw@kynesim.co.uk> [160316 10:14]:
> >  However, there are several possible options for a workaround if you are
> > using a 13MHz or 26MHz xtal - see
> > <http://www.ti.com/lit/er/sprz319f/sprz319f.pdf> PDF p111. It might perhaps
> > be civilised to give the user the option, since which is appropriate to your
> > board is a matter of board characterisation.
> 
> Seems like we should just configure dpll5 based on the SYS_CLK rate
> automatically.

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?

> > I see no shame in simply exposing something like:
> > 
> >  ti,omap3-dpll5-clock = < 443 5 8 >
> 
> If we want a binding like that it should be Linux generic and disucced
> on the linux-clk list. It's usually best to stick to standard
> bindings and hide the quirks in the driver(s). It seem at most we just
> need to specify the 36xx related compatible flag for dpll5.
> 
> > I have a Beagle xM or two here I can send out to anyone in need - throw me an
> > address.
>
> Cool, I think also Tomi Valkeinen was looking for a 36xx test board
> for occasional DSS testing. I have a beagle xm on loan here so I'm
> all set for now.

I'm considering this errata runtime tested enough, for now it would be nice
to have patch ready and verify that dd->mult_div1_reg contains the right
value ;-)
 
> >  OMAP3530 should also suffer from this problem, but it is not listed in the
> > errata sheet.
> > 
> >  Other than that, the only devices I know of that have this combination of
> > DPLL and HSUSB are OMAP3630 and DM3730 - if it's important, I can try and
> > find the appropriate people in TI?
> 
> AFAIK 3630 and dm3730 are exactly the same. The dm3730 is just the
> catalog part GP version.

Ok, let's prefix errata functions omap3630_ then.

Regards,
	ladis

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Tero Kristo March 22, 2016, 6:57 a.m. UTC | #1
On 03/22/2016 03:22 AM, Ladislav Michl wrote:
> On Thu, Mar 17, 2016 at 07:42:38AM -0700, Tony Lindgren wrote:
>> * Richard Watts <rrw@kynesim.co.uk> [160316 10:14]:
>>>   However, there are several possible options for a workaround if you are
>>> using a 13MHz or 26MHz xtal - see
>>> <http://www.ti.com/lit/er/sprz319f/sprz319f.pdf> PDF p111. It might perhaps
>>> be civilised to give the user the option, since which is appropriate to your
>>> board is a matter of board characterisation.
>>
>> Seems like we should just configure dpll5 based on the SYS_CLK rate
>> automatically.
>
> 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?

>
>>> I see no shame in simply exposing something like:
>>>
>>>   ti,omap3-dpll5-clock = < 443 5 8 >
>>
>> If we want a binding like that it should be Linux generic and disucced
>> on the linux-clk list. It's usually best to stick to standard
>> bindings and hide the quirks in the driver(s). It seem at most we just
>> need to specify the 36xx related compatible flag for dpll5.
>>
>>> I have a Beagle xM or two here I can send out to anyone in need - throw me an
>>> address.
>>
>> Cool, I think also Tomi Valkeinen was looking for a 36xx test board
>> for occasional DSS testing. I have a beagle xm on loan here so I'm
>> all set for now.
>
> I'm considering this errata runtime tested enough, for now it would be nice
> to have patch ready and verify that dd->mult_div1_reg contains the right
> value ;-)
>
>>>   OMAP3530 should also suffer from this problem, but it is not listed in the
>>> errata sheet.
>>>
>>>   Other than that, the only devices I know of that have this combination of
>>> DPLL and HSUSB are OMAP3630 and DM3730 - if it's important, I can try and
>>> find the appropriate people in TI?
>>
>> AFAIK 3630 and dm3730 are exactly the same. The dm3730 is just the
>> catalog part GP version.
>
> Ok, let's prefix errata functions omap3630_ then.
>
> Regards,
> 	ladis
>
> --- drivers/clk/ti/dpll.c.orig	2016-03-21 22:53:16.379746383 +0100
> +++ drivers/clk/ti/dpll.c	2016-03-22 01:48:36.988896607 +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,
> @@ -448,6 +460,7 @@
>   #ifdef CONFIG_ARCH_OMAP3
>   static void __init of_ti_omap3_dpll_setup(struct device_node *node)
>   {
> +	const struct clk_ops *ops = &omap3_dpll_ck_ops;
>   	const struct dpll_data dd = {
>   		.idlest_mask = 0x1,
>   		.enable_mask = 0x7,
> @@ -461,7 +474,10 @@
>   		.modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
>   	};
>
> -	of_ti_dpll_setup(node, &omap3_dpll_ck_ops, &dd);
> +	if (/* soc_is_omap3630() && */ strcmp(node->name, "dpll5_ck") == 0)
> +		ops = &omap3630_dpll_ck_ops;
> +
> +	of_ti_dpll_setup(node, ops, &dd);
>   }
>   CLK_OF_DECLARE(ti_omap3_dpll_clock, "ti,omap3-dpll-clock",
>   	       of_ti_omap3_dpll_setup);

Just add a new compatible:

 >   CLK_OF_DECLARE(ti_omap3630_dpll5_clock, "ti,omap3630-dpll5-clock",
 >   	       of_ti_omap3630_dpll5_setup);

Makes life simpler for everyone.

> --- 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);
>
> --- 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.

> +
> +	dd = clk->dpll_data;
> +	rate = target_rate == 120000000 ? *parent_rate : 0;
> +
> +	switch (rate) {
> +	case 13000000:
> +		dd->last_rounded_m = 443;
> +		dd->last_rounded_n = 5;
> +		break;
> +	case 26000000:
> +		dd->last_rounded_m = 443;
> +		dd->last_rounded_n = 11;
> +		break;

Add all the other frequencies listed in the errata here also.

> +	default:
> +		return omap2_dpll_round_rate(hw, target_rate, parent_rate);
> +	}
> +	/* actual divide value, value of the register is n - 1 */
> +	dd->last_rounded_n++;

This +1 you can just squash above within the switch statement.

> +	dd->last_rounded_rate = rate * dd->last_rounded_m / dd->last_rounded_n;
> +
> +	pr_debug("clock: %s: fixed m = %d, n = %d, new_rate = %lu\n",
> +		 clk_hw_get_name(hw), dd->last_rounded_m, dd->last_rounded_n,
> +		 dd->last_rounded_rate);
> +
> +	return dd->last_rounded_rate;
> +}
> --- 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;

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;
	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.


> +}
> +
>   /**
>    * omap3_noncore_dpll_set_parent - set parent for a DPLL clock
>    * @hw: pointer to the clock to set parent for
>

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

--- drivers/clk/ti/dpll.c.orig	2016-03-21 22:53:16.379746383 +0100
+++ drivers/clk/ti/dpll.c	2016-03-22 01:48:36.988896607 +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,
@@ -448,6 +460,7 @@ 
 #ifdef CONFIG_ARCH_OMAP3
 static void __init of_ti_omap3_dpll_setup(struct device_node *node)
 {
+	const struct clk_ops *ops = &omap3_dpll_ck_ops;
 	const struct dpll_data dd = {
 		.idlest_mask = 0x1,
 		.enable_mask = 0x7,
@@ -461,7 +474,10 @@ 
 		.modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
 	};
 
-	of_ti_dpll_setup(node, &omap3_dpll_ck_ops, &dd);
+	if (/* soc_is_omap3630() && */ strcmp(node->name, "dpll5_ck") == 0)
+		ops = &omap3630_dpll_ck_ops;
+
+	of_ti_dpll_setup(node, ops, &dd);
 }
 CLK_OF_DECLARE(ti_omap3_dpll_clock, "ti,omap3-dpll-clock",
 	       of_ti_omap3_dpll_setup);
--- 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);
 
--- 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;
+
+	dd = clk->dpll_data;
+	rate = target_rate == 120000000 ? *parent_rate : 0;
+
+	switch (rate) {
+	case 13000000:
+		dd->last_rounded_m = 443;
+		dd->last_rounded_n = 5;
+		break;
+	case 26000000:
+		dd->last_rounded_m = 443;
+		dd->last_rounded_n = 11;
+		break;
+	default:
+		return omap2_dpll_round_rate(hw, target_rate, parent_rate);
+	}
+	/* actual divide value, value of the register is n - 1 */
+	dd->last_rounded_n++;
+	dd->last_rounded_rate = rate * dd->last_rounded_m / dd->last_rounded_n;
+
+	pr_debug("clock: %s: fixed m = %d, n = %d, new_rate = %lu\n",
+		 clk_hw_get_name(hw), dd->last_rounded_m, dd->last_rounded_n,
+		 dd->last_rounded_rate);
+
+	return dd->last_rounded_rate;
+}
--- 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