diff mbox

clk: add specified-rate clock

Message ID 1368189862-17119-1-git-send-email-james.hogan@imgtec.com (mailing list archive)
State New, archived
Headers show

Commit Message

James Hogan May 10, 2013, 12:44 p.m. UTC
The frequency of some SoC's external oscillators (for example TZ1090's
XTAL1) are configured by the board using pull-ups/pull-downs of
configuration pins, the logic values of which are automatically latched
on reset and available in an SoC register. Add a generic clock component
and DT bindings to handle this.

It behaves similar to a fixed rate clock (read-only), except it needs
information about a register field (reg, shift, width), and the
clock-frequency is a mapping from register field values to clock
frequencies.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Mike Turquette <mturquette@linaro.org>
Cc: Grant Likely <grant.likely@secretlab.ca>
Cc: Rob Herring <rob.herring@calxeda.com>
Cc: Rob Landley <rob@landley.net>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
Cc: Lars-Peter Clausen <lars@metafoo.de>
---
 .../devicetree/bindings/clock/specified-clock.txt  |  39 ++++
 drivers/clk/Makefile                               |   1 +
 drivers/clk/clk-specified-rate.c                   | 201 +++++++++++++++++++++
 include/linux/clk-provider.h                       |  37 ++++
 4 files changed, 278 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/specified-clock.txt
 create mode 100644 drivers/clk/clk-specified-rate.c

Comments

Mike Turquette May 29, 2013, 5:39 p.m. UTC | #1
Quoting James Hogan (2013-05-10 05:44:22)
> The frequency of some SoC's external oscillators (for example TZ1090's
> XTAL1) are configured by the board using pull-ups/pull-downs of
> configuration pins, the logic values of which are automatically latched
> on reset and available in an SoC register. Add a generic clock component
> and DT bindings to handle this.
> 
> It behaves similar to a fixed rate clock (read-only), except it needs
> information about a register field (reg, shift, width), and the
> clock-frequency is a mapping from register field values to clock
> frequencies.
> 

James,

Thanks for sending this!  It looks mostly good and is a useful clock
type to support.  Comments below.

<snip>
> diff --git a/Documentation/devicetree/bindings/clock/specified-clock.txt b/Documentation/devicetree/bindings/clock/specified-clock.txt
> new file mode 100644
> index 0000000..b36ccf9
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/specified-clock.txt
> @@ -0,0 +1,39 @@
> +Binding for fixed-rate clock sources with readable configuration.
> +
> +This binding uses the common clock binding[1].
> +
> +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
> +
> +Required properties:
> +- compatible         : Shall be "specified-clock".
> +- #clock-cells       : From common clock binding; shall be set to 0.
> +- reg                : Address of configuration register.
> +- shift              : Shift of config value field in configuration register.
> +- width              : Width of config value field in configuration register.

It might be better to make this a mask instead of the width.  We have
already hit this issue with the mux table on Nvidia where arbitrary
masks are necessary.  Mask + shift probably helps future-proof us a bit.

The rest of the binding looks good.

<snip>
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index e7f7fe9..1343179 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -8,6 +8,7 @@ obj-$(CONFIG_COMMON_CLK)        += clk-fixed-rate.o
>  obj-$(CONFIG_COMMON_CLK)       += clk-gate.o
>  obj-$(CONFIG_COMMON_CLK)       += clk-mux.o
>  obj-$(CONFIG_COMMON_CLK)       += clk-composite.o
> +obj-$(CONFIG_COMMON_CLK)       += clk-specified-rate.o

One thing that does occur to me is that this could potentially be
combined with the fixed-rate clock.  If the properties for a specified
rate existing in the DT data then this code applies, otherwise the
existing fixed-rate code is used.  I don't have a strong opinion about
combining the code, but something to consider.

<snip>
> +#ifdef CONFIG_OF
> +/**
> + * of_specified_clk_setup() - Setup function for specified fixed rate clock
> + */
> +void __init of_specified_clk_setup(struct device_node *node)
> +{
> +       struct clk *clk;
> +       const char *clk_name = node->name;
> +       u32 shift, width, rate;
> +       void __iomem *reg;
> +       int len, num_rates, i;
> +       struct property *prop;
> +       struct clk_specified_rate_entry *rates;
> +       const __be32 *p;
> +
> +       of_property_read_string(node, "clock-output-names", &clk_name);
> +
> +       if (of_property_read_u32(node, "shift", &shift)) {
> +               pr_err("%s(%s): could not read shift property\n",
> +                      __func__, clk_name);
> +               return;
> +       }
> +
> +       if (of_property_read_u32(node, "width", &width)) {
> +               pr_err("%s(%s): could not read width property\n",
> +                      __func__, clk_name);
> +               return;
> +       }
> +
> +       reg = of_iomap(node, 0);
> +       if (!reg) {
> +               pr_err("%s(%s): of_iomap failed\n",
> +                      __func__, clk_name);
> +               return;
> +       }
> +
> +       /* check clock-frequency exists */
> +       prop = of_find_property(node, "clock-frequency", &len);
> +       if (!prop) {
> +               pr_err("%s(%s): could not find clock-frequency property\n",
> +                      __func__, clk_name);
> +               goto err_iounmap;
> +       }
> +
> +       if (len & (sizeof(u32)*2 - 1)) {
> +               pr_err("%s(%s): clock-frequency has invalid size of %d bytes\n",
> +                      __func__, clk_name, len);
> +               goto err_iounmap;
> +       }
> +       num_rates = len / (sizeof(*rates)*2);

This tripped me up for a few minutes.  I think it is a bit weird to
count bytes as a way to validate length and determine the number of
pairs.

I have written a binding for mux clocks which can have a table of
parents and the code below shows how I parsed the table:

+       u32 pair[2];
+
+       table_size = of_count_phandle_with_args(node, "table", "#clock-cells");
+
+       if (table_size < 1)
+               return NULL;
+
+       table = kzalloc(sizeof(struct clk_div_table) * table_size, GFP_KERNEL);
+       if (!table) {
+               pr_err("%s: unable to allocate memory for %s table\n", __func__, node->name);
+               return NULL;
+       }
+
+       for (i = 0; i < table_size; i++) {
+               if (!of_property_read_u32_array(node, "table", pair, ARRAY_SIZE(pair))) {
+                       table[i].val = pair[0];
+                       table[i].div = pair[1];
+               }
+       }

I'll be posting the mux-clock binding soon :-)

Regards,
Mike
James Hogan Nov. 13, 2013, 2:09 p.m. UTC | #2
On 29/05/13 18:39, Mike Turquette wrote:
> Quoting James Hogan (2013-05-10 05:44:22)
>> The frequency of some SoC's external oscillators (for example TZ1090's
>> XTAL1) are configured by the board using pull-ups/pull-downs of
>> configuration pins, the logic values of which are automatically latched
>> on reset and available in an SoC register. Add a generic clock component
>> and DT bindings to handle this.
>>
>> It behaves similar to a fixed rate clock (read-only), except it needs
>> information about a register field (reg, shift, width), and the
>> clock-frequency is a mapping from register field values to clock
>> frequencies.
>>
> 
> James,
> 
> Thanks for sending this!  It looks mostly good and is a useful clock
> type to support.  Comments below.

Hi Mike,

Sorry for slight delay getting back to you. I had another think about
this stuff yesterday...

> 
> <snip>
>> diff --git a/Documentation/devicetree/bindings/clock/specified-clock.txt b/Documentation/devicetree/bindings/clock/specified-clock.txt
>> new file mode 100644
>> index 0000000..b36ccf9
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/clock/specified-clock.txt
>> @@ -0,0 +1,39 @@
>> +Binding for fixed-rate clock sources with readable configuration.
>> +
>> +This binding uses the common clock binding[1].
>> +
>> +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
>> +
>> +Required properties:
>> +- compatible         : Shall be "specified-clock".
>> +- #clock-cells       : From common clock binding; shall be set to 0.
>> +- reg                : Address of configuration register.
>> +- shift              : Shift of config value field in configuration register.
>> +- width              : Width of config value field in configuration register.
> 
> It might be better to make this a mask instead of the width.  We have
> already hit this issue with the mux table on Nvidia where arbitrary
> masks are necessary.  Mask + shift probably helps future-proof us a bit.

Yes, thanks. I've now borrowed the bit-mask and bit-shift from your mux
binding proposals (including defaulting shift to ffs(mask)-1 ... nice).

> 
> The rest of the binding looks good.
> 
> <snip>
>> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
>> index e7f7fe9..1343179 100644
>> --- a/drivers/clk/Makefile
>> +++ b/drivers/clk/Makefile
>> @@ -8,6 +8,7 @@ obj-$(CONFIG_COMMON_CLK)        += clk-fixed-rate.o
>>  obj-$(CONFIG_COMMON_CLK)       += clk-gate.o
>>  obj-$(CONFIG_COMMON_CLK)       += clk-mux.o
>>  obj-$(CONFIG_COMMON_CLK)       += clk-composite.o
>> +obj-$(CONFIG_COMMON_CLK)       += clk-specified-rate.o
> 
> One thing that does occur to me is that this could potentially be
> combined with the fixed-rate clock.  If the properties for a specified
> rate existing in the DT data then this code applies, otherwise the
> existing fixed-rate code is used.  I don't have a strong opinion about
> combining the code, but something to consider.

That's actually a much neater solution. Because the clock is still
fixed, the register value can be read once while processing the DT node,
and an otherwise normal fixed rate clock created at the right frequency.
It doesn't even need to allocate memory to store the table.

The remaining question is whether to extend the fixed-clock binding or
have a separate one. I'm in two minds. On the one hand it is a
fixed-rate clock, on the other hand the only shared properties at the
moment would be standard clock properties (clock-output-names etc), so
I'm probably leaning towards a separate binding. I'll send an updated
patchset soon.

> 
> <snip>
>> +#ifdef CONFIG_OF
>> +/**
>> + * of_specified_clk_setup() - Setup function for specified fixed rate clock
>> + */
>> +void __init of_specified_clk_setup(struct device_node *node)
>> +{
>> +       struct clk *clk;
>> +       const char *clk_name = node->name;
>> +       u32 shift, width, rate;
>> +       void __iomem *reg;
>> +       int len, num_rates, i;
>> +       struct property *prop;
>> +       struct clk_specified_rate_entry *rates;
>> +       const __be32 *p;
>> +
>> +       of_property_read_string(node, "clock-output-names", &clk_name);
>> +
>> +       if (of_property_read_u32(node, "shift", &shift)) {
>> +               pr_err("%s(%s): could not read shift property\n",
>> +                      __func__, clk_name);
>> +               return;
>> +       }
>> +
>> +       if (of_property_read_u32(node, "width", &width)) {
>> +               pr_err("%s(%s): could not read width property\n",
>> +                      __func__, clk_name);
>> +               return;
>> +       }
>> +
>> +       reg = of_iomap(node, 0);
>> +       if (!reg) {
>> +               pr_err("%s(%s): of_iomap failed\n",
>> +                      __func__, clk_name);
>> +               return;
>> +       }
>> +
>> +       /* check clock-frequency exists */
>> +       prop = of_find_property(node, "clock-frequency", &len);
>> +       if (!prop) {
>> +               pr_err("%s(%s): could not find clock-frequency property\n",
>> +                      __func__, clk_name);
>> +               goto err_iounmap;
>> +       }
>> +
>> +       if (len & (sizeof(u32)*2 - 1)) {
>> +               pr_err("%s(%s): clock-frequency has invalid size of %d bytes\n",
>> +                      __func__, clk_name, len);
>> +               goto err_iounmap;
>> +       }
>> +       num_rates = len / (sizeof(*rates)*2);
> 
> This tripped me up for a few minutes.  I think it is a bit weird to
> count bytes as a way to validate length and determine the number of
> pairs.

Yes, and I actually recently discovered that this is wrong anyway. rates
is a struct of two elements so it ends up dividing twice, and it just so
happened that all the boards I have (until I hacked up qemu to report a
different frequency) specify a value in the first half of those defined. :)

Thanks
James
Tomasz Figa Nov. 13, 2013, 2:18 p.m. UTC | #3
Hi James, Mike,

On Wednesday 13 of November 2013 14:09:56 James Hogan wrote:
> On 29/05/13 18:39, Mike Turquette wrote:
> > Quoting James Hogan (2013-05-10 05:44:22)
> >> The frequency of some SoC's external oscillators (for example TZ1090's
> >> XTAL1) are configured by the board using pull-ups/pull-downs of
> >> configuration pins, the logic values of which are automatically latched
> >> on reset and available in an SoC register. Add a generic clock component
> >> and DT bindings to handle this.
> >>
> >> It behaves similar to a fixed rate clock (read-only), except it needs
> >> information about a register field (reg, shift, width), and the
> >> clock-frequency is a mapping from register field values to clock
> >> frequencies.
> >>
> > 
> > James,
> > 
> > Thanks for sending this!  It looks mostly good and is a useful clock
> > type to support.  Comments below.
> 
> Hi Mike,
> 
> Sorry for slight delay getting back to you. I had another think about
> this stuff yesterday...
> 

Just a random idea that came to my mind while reading this thread:

What about modelling this as a set of fixed rate clocks fed into
a read-only mux?

Best regards,
Tomasz

> > 
> > <snip>
> >> diff --git a/Documentation/devicetree/bindings/clock/specified-clock.txt b/Documentation/devicetree/bindings/clock/specified-clock.txt
> >> new file mode 100644
> >> index 0000000..b36ccf9
> >> --- /dev/null
> >> +++ b/Documentation/devicetree/bindings/clock/specified-clock.txt
> >> @@ -0,0 +1,39 @@
> >> +Binding for fixed-rate clock sources with readable configuration.
> >> +
> >> +This binding uses the common clock binding[1].
> >> +
> >> +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
> >> +
> >> +Required properties:
> >> +- compatible         : Shall be "specified-clock".
> >> +- #clock-cells       : From common clock binding; shall be set to 0.
> >> +- reg                : Address of configuration register.
> >> +- shift              : Shift of config value field in configuration register.
> >> +- width              : Width of config value field in configuration register.
> > 
> > It might be better to make this a mask instead of the width.  We have
> > already hit this issue with the mux table on Nvidia where arbitrary
> > masks are necessary.  Mask + shift probably helps future-proof us a bit.
> 
> Yes, thanks. I've now borrowed the bit-mask and bit-shift from your mux
> binding proposals (including defaulting shift to ffs(mask)-1 ... nice).
> 
> > 
> > The rest of the binding looks good.
> > 
> > <snip>
> >> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> >> index e7f7fe9..1343179 100644
> >> --- a/drivers/clk/Makefile
> >> +++ b/drivers/clk/Makefile
> >> @@ -8,6 +8,7 @@ obj-$(CONFIG_COMMON_CLK)        += clk-fixed-rate.o
> >>  obj-$(CONFIG_COMMON_CLK)       += clk-gate.o
> >>  obj-$(CONFIG_COMMON_CLK)       += clk-mux.o
> >>  obj-$(CONFIG_COMMON_CLK)       += clk-composite.o
> >> +obj-$(CONFIG_COMMON_CLK)       += clk-specified-rate.o
> > 
> > One thing that does occur to me is that this could potentially be
> > combined with the fixed-rate clock.  If the properties for a specified
> > rate existing in the DT data then this code applies, otherwise the
> > existing fixed-rate code is used.  I don't have a strong opinion about
> > combining the code, but something to consider.
> 
> That's actually a much neater solution. Because the clock is still
> fixed, the register value can be read once while processing the DT node,
> and an otherwise normal fixed rate clock created at the right frequency.
> It doesn't even need to allocate memory to store the table.
> 
> The remaining question is whether to extend the fixed-clock binding or
> have a separate one. I'm in two minds. On the one hand it is a
> fixed-rate clock, on the other hand the only shared properties at the
> moment would be standard clock properties (clock-output-names etc), so
> I'm probably leaning towards a separate binding. I'll send an updated
> patchset soon.
> 
> > 
> > <snip>
> >> +#ifdef CONFIG_OF
> >> +/**
> >> + * of_specified_clk_setup() - Setup function for specified fixed rate clock
> >> + */
> >> +void __init of_specified_clk_setup(struct device_node *node)
> >> +{
> >> +       struct clk *clk;
> >> +       const char *clk_name = node->name;
> >> +       u32 shift, width, rate;
> >> +       void __iomem *reg;
> >> +       int len, num_rates, i;
> >> +       struct property *prop;
> >> +       struct clk_specified_rate_entry *rates;
> >> +       const __be32 *p;
> >> +
> >> +       of_property_read_string(node, "clock-output-names", &clk_name);
> >> +
> >> +       if (of_property_read_u32(node, "shift", &shift)) {
> >> +               pr_err("%s(%s): could not read shift property\n",
> >> +                      __func__, clk_name);
> >> +               return;
> >> +       }
> >> +
> >> +       if (of_property_read_u32(node, "width", &width)) {
> >> +               pr_err("%s(%s): could not read width property\n",
> >> +                      __func__, clk_name);
> >> +               return;
> >> +       }
> >> +
> >> +       reg = of_iomap(node, 0);
> >> +       if (!reg) {
> >> +               pr_err("%s(%s): of_iomap failed\n",
> >> +                      __func__, clk_name);
> >> +               return;
> >> +       }
> >> +
> >> +       /* check clock-frequency exists */
> >> +       prop = of_find_property(node, "clock-frequency", &len);
> >> +       if (!prop) {
> >> +               pr_err("%s(%s): could not find clock-frequency property\n",
> >> +                      __func__, clk_name);
> >> +               goto err_iounmap;
> >> +       }
> >> +
> >> +       if (len & (sizeof(u32)*2 - 1)) {
> >> +               pr_err("%s(%s): clock-frequency has invalid size of %d bytes\n",
> >> +                      __func__, clk_name, len);
> >> +               goto err_iounmap;
> >> +       }
> >> +       num_rates = len / (sizeof(*rates)*2);
> > 
> > This tripped me up for a few minutes.  I think it is a bit weird to
> > count bytes as a way to validate length and determine the number of
> > pairs.
> 
> Yes, and I actually recently discovered that this is wrong anyway. rates
> is a struct of two elements so it ends up dividing twice, and it just so
> happened that all the boards I have (until I hacked up qemu to report a
> different frequency) specify a value in the first half of those defined. :)
> 
> Thanks
> James
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
James Hogan Nov. 13, 2013, 2:31 p.m. UTC | #4
On 13/11/13 14:18, Tomasz Figa wrote:
> Hi James, Mike,
> 
> On Wednesday 13 of November 2013 14:09:56 James Hogan wrote:
>> On 29/05/13 18:39, Mike Turquette wrote:
>>> Quoting James Hogan (2013-05-10 05:44:22)
>>>> The frequency of some SoC's external oscillators (for example TZ1090's
>>>> XTAL1) are configured by the board using pull-ups/pull-downs of
>>>> configuration pins, the logic values of which are automatically latched
>>>> on reset and available in an SoC register. Add a generic clock component
>>>> and DT bindings to handle this.
>>>>
>>>> It behaves similar to a fixed rate clock (read-only), except it needs
>>>> information about a register field (reg, shift, width), and the
>>>> clock-frequency is a mapping from register field values to clock
>>>> frequencies.
>>>>
>>>
>>> James,
>>>
>>> Thanks for sending this!  It looks mostly good and is a useful clock
>>> type to support.  Comments below.
>>
>> Hi Mike,
>>
>> Sorry for slight delay getting back to you. I had another think about
>> this stuff yesterday...
>>
> 
> Just a random idea that came to my mind while reading this thread:
> 
> What about modelling this as a set of fixed rate clocks fed into
> a read-only mux?

Yes, that had occurred to me too. I suppose the arguments against would be:
* it doesn't describe the hardware, there is no mux, just a fixed rate
clock with a discoverable frequency.
* it would sort of work for my small case of only having 9 possible
frequencies (although it would be a bit verbose), but wouldn't scale
nicely or be extendible to if the frequency was encoded more
continuously in the register value. E.g. if the frequency was 1MHz *
(the register value - 1) or something crazy like that. Of course that's
conjecture and SoC designers probably aren't going to want to use more
pins for bootstrap config than necessary.

Cheers
James
Tomasz Figa Nov. 13, 2013, 2:38 p.m. UTC | #5
On Wednesday 13 of November 2013 14:31:25 James Hogan wrote:
> On 13/11/13 14:18, Tomasz Figa wrote:
> > Hi James, Mike,
> > 
> > On Wednesday 13 of November 2013 14:09:56 James Hogan wrote:
> >> On 29/05/13 18:39, Mike Turquette wrote:
> >>> Quoting James Hogan (2013-05-10 05:44:22)
> >>>> The frequency of some SoC's external oscillators (for example TZ1090's
> >>>> XTAL1) are configured by the board using pull-ups/pull-downs of
> >>>> configuration pins, the logic values of which are automatically latched
> >>>> on reset and available in an SoC register. Add a generic clock component
> >>>> and DT bindings to handle this.
> >>>>
> >>>> It behaves similar to a fixed rate clock (read-only), except it needs
> >>>> information about a register field (reg, shift, width), and the
> >>>> clock-frequency is a mapping from register field values to clock
> >>>> frequencies.
> >>>>
> >>>
> >>> James,
> >>>
> >>> Thanks for sending this!  It looks mostly good and is a useful clock
> >>> type to support.  Comments below.
> >>
> >> Hi Mike,
> >>
> >> Sorry for slight delay getting back to you. I had another think about
> >> this stuff yesterday...
> >>
> > 
> > Just a random idea that came to my mind while reading this thread:
> > 
> > What about modelling this as a set of fixed rate clocks fed into
> > a read-only mux?
> 
> Yes, that had occurred to me too. I suppose the arguments against would be:
> * it doesn't describe the hardware, there is no mux, just a fixed rate
> clock with a discoverable frequency.

For me, a set of configuration pins that select clock frequency sounds
like a mux. I'd rather say that there are no physical clocks that would
be its inputs, but that's probably just an implementation detail.

> * it would sort of work for my small case of only having 9 possible
> frequencies (although it would be a bit verbose), but wouldn't scale
> nicely or be extendible to if the frequency was encoded more
> continuously in the register value. E.g. if the frequency was 1MHz *
> (the register value - 1) or something crazy like that.

I guess you would need an implementation specific code to calculate the
frequency in such cases, which is not what you suggested above (but
rather "a mapping from register field values to clock frequencies"
inside a property).

Best regards,
Tomasz
James Hogan Nov. 13, 2013, 3:18 p.m. UTC | #6
On 13/11/13 14:38, Tomasz Figa wrote:
> On Wednesday 13 of November 2013 14:31:25 James Hogan wrote:
>> On 13/11/13 14:18, Tomasz Figa wrote:
>>> Hi James, Mike,
>>>
>>> On Wednesday 13 of November 2013 14:09:56 James Hogan wrote:
>>>> On 29/05/13 18:39, Mike Turquette wrote:
>>>>> Quoting James Hogan (2013-05-10 05:44:22)
>>>>>> The frequency of some SoC's external oscillators (for example TZ1090's
>>>>>> XTAL1) are configured by the board using pull-ups/pull-downs of
>>>>>> configuration pins, the logic values of which are automatically latched
>>>>>> on reset and available in an SoC register. Add a generic clock component
>>>>>> and DT bindings to handle this.
>>>>>>
>>>>>> It behaves similar to a fixed rate clock (read-only), except it needs
>>>>>> information about a register field (reg, shift, width), and the
>>>>>> clock-frequency is a mapping from register field values to clock
>>>>>> frequencies.
>>>>>>
>>>>>
>>>>> James,
>>>>>
>>>>> Thanks for sending this!  It looks mostly good and is a useful clock
>>>>> type to support.  Comments below.
>>>>
>>>> Hi Mike,
>>>>
>>>> Sorry for slight delay getting back to you. I had another think about
>>>> this stuff yesterday...
>>>>
>>>
>>> Just a random idea that came to my mind while reading this thread:
>>>
>>> What about modelling this as a set of fixed rate clocks fed into
>>> a read-only mux?
>>
>> Yes, that had occurred to me too. I suppose the arguments against would be:
>> * it doesn't describe the hardware, there is no mux, just a fixed rate
>> clock with a discoverable frequency.
> 
> For me, a set of configuration pins that select clock frequency sounds
> like a mux. I'd rather say that there are no physical clocks that would
> be its inputs, but that's probably just an implementation detail.

Sure, it sounds a bit like one and has a similar register interface, but
it's still not one. The hardware to latch the pins on reset and present
the logic values in a register is independent of the hardware to get the
clock signal from the fixed rate oscillator on the board into the SoC.

>> * it would sort of work for my small case of only having 9 possible
>> frequencies (although it would be a bit verbose), but wouldn't scale
>> nicely or be extendible to if the frequency was encoded more
>> continuously in the register value. E.g. if the frequency was 1MHz *
>> (the register value - 1) or something crazy like that.
> 
> I guess you would need an implementation specific code to calculate the
> frequency in such cases, which is not what you suggested above (but
> rather "a mapping from register field values to clock frequencies"
> inside a property).

Indeed. It's worth noting though that we already have a variety of such
variations handled by the generic clock divider driver (table based
lookup, one based, power of two, etc), so we should probably allow for
others to extend this later too.

Cheers
James
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/clock/specified-clock.txt b/Documentation/devicetree/bindings/clock/specified-clock.txt
new file mode 100644
index 0000000..b36ccf9
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/specified-clock.txt
@@ -0,0 +1,39 @@ 
+Binding for fixed-rate clock sources with readable configuration.
+
+This binding uses the common clock binding[1].
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Required properties:
+- compatible         : Shall be "specified-clock".
+- #clock-cells       : From common clock binding; shall be set to 0.
+- reg                : Address of configuration register.
+- shift              : Shift of config value field in configuration register.
+- width              : Width of config value field in configuration register.
+- clock-frequency    : Frequency mapping of clock. Consecutive pairs of cells
+                       represent the config value to match and the clock
+                       frequency in Hz for that config value.
+
+Optional properties:
+- clock-output-names : From common clock binding.
+
+Example:
+	clock {
+		compatible = "specified-clock";
+		#clock-cells = <0>;
+		reg = <0x02004004 0x4>;	/* CR_PERIP_RESET_CFG */
+		shift = <8>;		/* FXTAL */
+		width = <4>;
+		clock-frequency =
+		/*	 FXTAL	Frequency */
+			<0	16384000>,
+			<1	19200000>,
+			<2	24000000>,
+			<3	24576000>,
+			<4	26000000>,
+			<5	36000000>,
+			<6	36864000>,
+			<7	38400000>,
+			<8	40000000>;
+		clock-output-names = "xtal1";
+	};
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index e7f7fe9..1343179 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -8,6 +8,7 @@  obj-$(CONFIG_COMMON_CLK)	+= clk-fixed-rate.o
 obj-$(CONFIG_COMMON_CLK)	+= clk-gate.o
 obj-$(CONFIG_COMMON_CLK)	+= clk-mux.o
 obj-$(CONFIG_COMMON_CLK)	+= clk-composite.o
+obj-$(CONFIG_COMMON_CLK)	+= clk-specified-rate.o
 
 # SoCs specific
 obj-$(CONFIG_ARCH_BCM2835)	+= clk-bcm2835.o
diff --git a/drivers/clk/clk-specified-rate.c b/drivers/clk/clk-specified-rate.c
new file mode 100644
index 0000000..8f78033
--- /dev/null
+++ b/drivers/clk/clk-specified-rate.c
@@ -0,0 +1,201 @@ 
+/*
+ * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
+ * Copyright (C) 2013 Imagination Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Fixed rate clock implementation with rate specified in a register field.
+ * Based on fixed rate clock implementation.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+
+/*
+ * DOC: basic specified-rate clock that cannot gate
+ *
+ * Traits of this clock:
+ * prepare - clk_(un)prepare only ensures parents are prepared
+ * enable - clk_enable only ensures parents are enabled
+ * rate - rate is always a fixed value.  No clk_set_rate support
+ * parent - fixed parent.  No clk_set_parent support
+ */
+
+#define to_clk_specified_rate(_hw) \
+	container_of(_hw, struct clk_specified_rate, hw)
+
+static unsigned long clk_specified_rate_recalc_rate(struct clk_hw *hw,
+		unsigned long parent_rate)
+{
+	struct clk_specified_rate *specified = to_clk_specified_rate(hw);
+	struct clk_specified_rate_entry *entry;
+	u32 val;
+	unsigned int i;
+
+	/* read configuration field */
+	val = readl(specified->reg);
+	val >>= specified->shift;
+	val &= (1 << specified->width) - 1;
+
+	/* match the value in the mapping */
+	for (i = 0; i < specified->num_rates; ++i) {
+		entry = &specified->rates[i];
+		if (val == entry->value)
+			return entry->rate;
+	}
+
+	/* unknown rate! */
+	return 0;
+}
+
+const struct clk_ops clk_specified_rate_ops = {
+	.recalc_rate = clk_specified_rate_recalc_rate,
+};
+EXPORT_SYMBOL_GPL(clk_specified_rate_ops);
+
+/**
+ * clk_register_specified_rate - register specified-rate clock
+ * @dev:		device that is registering this clock
+ * @name:		name of this clock
+ * @parent_name:	name of clock's parent
+ * @flags:		framework-specific flags
+ * @reg:		config register
+ * @shift:		shift into config register of frequency field
+ * @width:		width of frequency field in config register
+ * @rates:		value->rate mapping entries
+ * @num_rates:		number of rates in @rates
+ */
+struct clk *clk_register_specified_rate(struct device *dev, const char *name,
+		const char *parent_name, unsigned long flags,
+		void __iomem *reg, u8 shift, u8 width,
+		struct clk_specified_rate_entry *rates,
+		unsigned long num_rates)
+{
+	struct clk_specified_rate *specified;
+	struct clk *clk;
+	struct clk_init_data init;
+
+	/* allocate specified-rate clock */
+	specified = kzalloc(sizeof(struct clk_specified_rate), GFP_KERNEL);
+	if (!specified) {
+		pr_err("%s(%s): could not allocate specified clk\n",
+		       __func__, name);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	init.name = name;
+	init.ops = &clk_specified_rate_ops;
+	init.flags = flags | CLK_IS_BASIC;
+	init.parent_names = (parent_name ? &parent_name : NULL);
+	init.num_parents = (parent_name ? 1 : 0);
+
+	/* struct clk_specified_rate assignments */
+	specified->reg = reg;
+	specified->shift = shift;
+	specified->width = width;
+	specified->rates = rates;
+	specified->num_rates = num_rates;
+	specified->hw.init = &init;
+
+	/* register the clock */
+	clk = clk_register(dev, &specified->hw);
+
+	if (IS_ERR(clk))
+		kfree(specified);
+
+	return clk;
+}
+
+#ifdef CONFIG_OF
+/**
+ * of_specified_clk_setup() - Setup function for specified fixed rate clock
+ */
+void __init of_specified_clk_setup(struct device_node *node)
+{
+	struct clk *clk;
+	const char *clk_name = node->name;
+	u32 shift, width, rate;
+	void __iomem *reg;
+	int len, num_rates, i;
+	struct property *prop;
+	struct clk_specified_rate_entry *rates;
+	const __be32 *p;
+
+	of_property_read_string(node, "clock-output-names", &clk_name);
+
+	if (of_property_read_u32(node, "shift", &shift)) {
+		pr_err("%s(%s): could not read shift property\n",
+		       __func__, clk_name);
+		return;
+	}
+
+	if (of_property_read_u32(node, "width", &width)) {
+		pr_err("%s(%s): could not read width property\n",
+		       __func__, clk_name);
+		return;
+	}
+
+	reg = of_iomap(node, 0);
+	if (!reg) {
+		pr_err("%s(%s): of_iomap failed\n",
+		       __func__, clk_name);
+		return;
+	}
+
+	/* check clock-frequency exists */
+	prop = of_find_property(node, "clock-frequency", &len);
+	if (!prop) {
+		pr_err("%s(%s): could not find clock-frequency property\n",
+		       __func__, clk_name);
+		goto err_iounmap;
+	}
+
+	if (len & (sizeof(u32)*2 - 1)) {
+		pr_err("%s(%s): clock-frequency has invalid size of %d bytes\n",
+		       __func__, clk_name, len);
+		goto err_iounmap;
+	}
+	num_rates = len / (sizeof(*rates)*2);
+
+	rates = kzalloc(sizeof(*rates)*num_rates, GFP_KERNEL);
+	if (!rates) {
+		pr_err("%s(%s): could not allocate %d rate mapping entries\n",
+		       __func__, clk_name, num_rates);
+		goto err_iounmap;
+	}
+
+	/* extract rate mapping */
+	for (i = 0, p = of_prop_next_u32(prop, NULL, &rates[0].value);
+	     p;
+	     ++i, p = of_prop_next_u32(prop, p, &rates[i].value)) {
+		p = of_prop_next_u32(prop, p, &rate);
+		rates[i].rate = rate;
+		pr_debug("%s(%s): map %u -> %lu Hz\n",
+			 __func__, clk_name, rates[i].value, rates[i].rate);
+	}
+
+	clk = clk_register_specified_rate(NULL, clk_name, NULL, CLK_IS_ROOT,
+					  reg, shift, width, rates, num_rates);
+	if (IS_ERR(clk))
+		goto err_kfree;
+
+	of_clk_add_provider(node, of_clk_src_simple_get, clk);
+
+	return;
+
+err_kfree:
+	kfree(rates);
+err_iounmap:
+	iounmap(reg);
+}
+EXPORT_SYMBOL_GPL(of_specified_clk_setup);
+CLK_OF_DECLARE(specified_clk, "specified-clock", of_specified_clk_setup);
+#endif
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 1186098..218a406 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -196,6 +196,43 @@  struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
 void of_fixed_clk_setup(struct device_node *np);
 
 /**
+ * struct clk_specified_rate_entry - a single possible specified rate
+ * @value:	value to match in config register
+ * @rate:	rate to use when config register matches @value
+ */
+struct clk_specified_rate_entry {
+	u32		value;
+	unsigned long	rate;
+};
+
+/**
+ * struct clk_specified_rate - specified-rate clock
+ * @hw:		handle between common and hardware-specific interfaces
+ * @reg:	register containing rate specifier field
+ * @shift:	shift to rate specifier field
+ * @width:	width of rate specifier field
+ * @rates:	mapping of specified frequencies
+ * @num_rates:	number of rates in array
+ */
+struct clk_specified_rate {
+	struct		clk_hw hw;
+	void __iomem	*reg;
+	u8		shift;
+	u8		width;
+	struct clk_specified_rate_entry	*rates;
+	unsigned int	num_rates;
+};
+
+extern const struct clk_ops clk_specified_rate_ops;
+struct clk *clk_register_specified_rate(struct device *dev, const char *name,
+		const char *parent_names, unsigned long flags,
+		void __iomem *reg, u8 shift, u8 width,
+		struct clk_specified_rate_entry *rates,
+		unsigned long num_rates);
+
+void of_specified_clk_setup(struct device_node *np);
+
+/**
  * struct clk_gate - gating clock
  *
  * @hw:		handle between common and hardware-specific interfaces