diff mbox

[v5,4/4] clk: bd71837: Add driver for BD71837 PMIC clock

Message ID d99c8762b0fbbcb18ec4f4d104191364c0ea798c.1528117485.git.matti.vaittinen@fi.rohmeurope.com (mailing list archive)
State Changes Requested, archived
Headers show

Commit Message

Vaittinen, Matti June 4, 2018, 1:19 p.m. UTC
Support BD71837 gateable 32768 Hz clock.

Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
---
 drivers/clk/Kconfig       |   7 +++
 drivers/clk/Makefile      |   1 +
 drivers/clk/clk-bd71837.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 154 insertions(+)
 create mode 100644 drivers/clk/clk-bd71837.c

Comments

Stephen Boyd June 12, 2018, 7:44 a.m. UTC | #1
Quoting Matti Vaittinen (2018-06-04 06:19:13)
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index 41492e980ef4..e693496f202a 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -279,6 +279,13 @@ config COMMON_CLK_STM32H7
>         ---help---
>           Support for stm32h7 SoC family clocks
>  
> +config COMMON_CLK_BD71837
> +       tristate "Clock driver for ROHM BD71837 PMIC MFD"
> +       depends on MFD_BD71837
> +       help
> +         This driver supports ROHM BD71837 PMIC clock.
> +
> +

Drop one newline above.

>  source "drivers/clk/bcm/Kconfig"
>  source "drivers/clk/hisilicon/Kconfig"
>  source "drivers/clk/imgtec/Kconfig"
> diff --git a/drivers/clk/clk-bd71837.c b/drivers/clk/clk-bd71837.c
> new file mode 100644
> index 000000000000..5ba6c05c5a98
> --- /dev/null
> +++ b/drivers/clk/clk-bd71837.c
> @@ -0,0 +1,146 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (C) 2018 ROHM Semiconductors
> +// bd71837.c  -- ROHM BD71837MWV clock driver
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/err.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/mfd/bd71837.h>
> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.h>
> +
> +

Drop one newline above.

> +struct bd71837_clk {
> +       struct clk_hw hw;
> +       uint8_t reg;
> +       uint8_t mask;

Use u8 instead of uint8_t.

> +       unsigned long rate;
> +       struct platform_device *pdev;
> +       struct bd71837 *mfd;
> +};
> +
> +static int bd71837_clk_set(struct clk_hw *hw, int status)
> +{
> +       struct bd71837_clk *c = container_of(hw, struct bd71837_clk, hw);
> +
> +       return bd71837_update_bits(c->mfd, c->reg, c->mask, status);

Any reason we can't use a regmap?

> +}
> +
> +static void bd71837_clk_disable(struct clk_hw *hw)
> +{
> +       int rv;
> +       struct bd71837_clk *c = container_of(hw, struct bd71837_clk, hw);
> +
> +       rv = bd71837_clk_set(hw, 0);
> +       if (rv)
> +               dev_dbg(&c->pdev->dev, "Failed to disable 32K clk (%d)\n", rv);
> +}
> +
> +static int bd71837_clk_enable(struct clk_hw *hw)
> +{
> +       return bd71837_clk_set(hw, 1);
> +}
> +
> +static int bd71837_clk_is_enabled(struct clk_hw *hw)
> +{
> +       struct bd71837_clk *c = container_of(hw, struct bd71837_clk, hw);
> +
> +       return c->mask & bd71837_reg_read(c->mfd, c->reg);

Didn't I ask for local variable for reg_read result?

> +}
> +
> +static unsigned long bd71837_clk_recalc_rate(struct clk_hw *hw,
> +                                            unsigned long parent_rate)
> +{
> +       struct bd71837_clk *c = container_of(hw, struct bd71837_clk, hw);
> +
> +       return c->rate;
> +}
> +
> +static const struct clk_ops bd71837_clk_ops = {
> +       .recalc_rate = &bd71837_clk_recalc_rate,
> +       .prepare = &bd71837_clk_enable,
> +       .unprepare = &bd71837_clk_disable,
> +       .is_prepared = &bd71837_clk_is_enabled,
> +};
> +
> +static int bd71837_clk_probe(struct platform_device *pdev)
> +{
> +       struct bd71837_clk *c;
> +       int rval = -ENOMEM;
> +       struct bd71837 *mfd = dev_get_drvdata(pdev->dev.parent);
> +       struct clk_init_data init = {
> +               .name = "bd71837-32k-out",
> +               .ops = &bd71837_clk_ops,
> +       };
> +
> +       c = devm_kzalloc(&pdev->dev, sizeof(*c), GFP_KERNEL);
> +       if (!c)
> +               goto err_out;
> +
> +       c->reg = BD71837_REG_OUT32K;
> +       c->mask = BD71837_OUT32K_EN;
> +       c->rate = BD71837_CLK_RATE;

The PMIC has an 'XIN' pin that would be the clk input for this chip, and
the output clk, this driver, would specify that xin clock as the parent.
The 'xin' clock would then be listed in DT as a fixed-rate clock. That
way this driver doesn't hardcode the frequency.

> +       c->mfd = mfd;
> +       c->pdev = pdev;
> +
> +       of_property_read_string_index(pdev->dev.parent->of_node,
> +                                             "clock-output-names", 0,
> +                                             &init.name);
> +
> +       c->hw.init = &init;

Do this next to all the other c-> things?

> +
> +       rval = devm_clk_hw_register(&pdev->dev, &c->hw);
> +       if (rval) {
> +               dev_err(&pdev->dev, "failed to register 32K clk");
> +               goto err_out;
> +       }
> +
> +       if (pdev->dev.parent->of_node) {
> +               rval = of_clk_add_hw_provider(pdev->dev.parent->of_node,
> +                                            of_clk_hw_simple_get,
> +                                            &c->hw);
> +               if (rval) {
> +                       dev_err(&pdev->dev, "adding clk provider failed\n");
> +                       goto err_out;

Just return rval instead of goto and then remove err_out label.

> +               }
> +       }
> +
> +       rval = clk_hw_register_clkdev(&c->hw, init.name, NULL);

This leaks a clkdev lookup. Sad we don't have a devm version. Maybe it
should be added. But I really doubt this chip will be used with clkdev
lookups so please remove clkdev until you have a user who needs it.

> +       if (rval) {
> +               dev_err(&pdev->dev, "failed to register clkdev for bd71837");
> +               goto err_clean_provider;
> +       }
> +
> +       platform_set_drvdata(pdev, c);
> +
> +       return 0;
> +
> +err_clean_provider:
> +       of_clk_del_provider(pdev->dev.parent->of_node);
> +err_out:
> +       return rval;
> +}
> +
> +static int bd71837_clk_remove(struct platform_device *pdev)
> +{
> +       if (pdev->dev.parent->of_node)
> +               of_clk_del_provider(pdev->dev.parent->of_node);

Use devm so this can go away. Or is devm not used because the parent
of_node is the provider? That's annoying.

> +       return 0;
> +}
> +
--
To unsubscribe from this list: send the line "unsubscribe linux-clk" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Matti Vaittinen June 12, 2018, 8:23 a.m. UTC | #2
Hello Stephen,

Thanks again for the review. I'll do new patch which fixes these issues.

On Tue, Jun 12, 2018 at 12:44:11AM -0700, Stephen Boyd wrote:
> Quoting Matti Vaittinen (2018-06-04 06:19:13)
> > +config COMMON_CLK_BD71837
> > +       tristate "Clock driver for ROHM BD71837 PMIC MFD"
> > +       depends on MFD_BD71837
> > +       help
> > +         This driver supports ROHM BD71837 PMIC clock.
> > +
> > +
> 
> Drop one newline above.
Right.

> 
> > +++ b/drivers/clk/clk-bd71837.c
> > +#include <linux/mfd/bd71837.h>
> > +#include <linux/clk-provider.h>
> > +#include <linux/clkdev.h>
> > +
> > +
> 
> Drop one newline above.
Right.

> 
> > +struct bd71837_clk {
> > +       struct clk_hw hw;
> > +       uint8_t reg;
> > +       uint8_t mask;
> 
> Use u8 instead of uint8_t.
I can do that but I am afraid I have missed the reason for this. Why u8
is preferred? I would have assumed the standard uint8_t would be
preferred - can you please educate me as to what is this reason?

> > +       unsigned long rate;
> > +       struct platform_device *pdev;
> > +       struct bd71837 *mfd;
> > +};
> > +
> > +static int bd71837_clk_set(struct clk_hw *hw, int status)
> > +{
> > +       struct bd71837_clk *c = container_of(hw, struct bd71837_clk, hw);
> > +
> > +       return bd71837_update_bits(c->mfd, c->reg, c->mask, status);
> 
> Any reason we can't use a regmap?

Not really. These wrappers might become handy if the next chip in series
requires some quirks for access but at the moment I see no reason for
the wrappers. I could switch to direct regmap calls but that change
should then be done to all subdevices and not just for clk one. This
means I should rework also the already applied regulator part. I have
some other changes pending for regulators (adding support for next chip,
renaming bd71837 to bd718x7 or bd718xx, and few comments from  Andy
Shevchenko and Rob Herring). I would rather switch to direct regmap usage
for all subdevices at the same time. So if it is acceptable to keep the
wrappers for now (and then create own patch set to remove them from all
subdevices and the header) I would like to do so.

> 
> > +}
> > +
> > +static void bd71837_clk_disable(struct clk_hw *hw)
> > +{
> > +       int rv;
> > +       struct bd71837_clk *c = container_of(hw, struct bd71837_clk, hw);
> > +
> > +       rv = bd71837_clk_set(hw, 0);
> > +       if (rv)
> > +               dev_dbg(&c->pdev->dev, "Failed to disable 32K clk (%d)\n", rv);
> > +}
> > +
> > +static int bd71837_clk_enable(struct clk_hw *hw)
> > +{
> > +       return bd71837_clk_set(hw, 1);
> > +}
> > +
> > +static int bd71837_clk_is_enabled(struct clk_hw *hw)
> > +{
> > +       struct bd71837_clk *c = container_of(hw, struct bd71837_clk, hw);
> > +
> > +       return c->mask & bd71837_reg_read(c->mfd, c->reg);
> 
> Didn't I ask for local variable for reg_read result?

Yes! Sorry! And thanks for pointing this out again. It seems I missed
this one.

> 
> > +}
> > +
> > +static unsigned long bd71837_clk_recalc_rate(struct clk_hw *hw,
> > +                                            unsigned long parent_rate)
> > +{
> > +       struct bd71837_clk *c = container_of(hw, struct bd71837_clk, hw);
> > +
> > +       return c->rate;
> > +}
> > +
> > +static const struct clk_ops bd71837_clk_ops = {
> > +       .recalc_rate = &bd71837_clk_recalc_rate,
> > +       .prepare = &bd71837_clk_enable,
> > +       .unprepare = &bd71837_clk_disable,
> > +       .is_prepared = &bd71837_clk_is_enabled,
> > +};
> > +
> > +static int bd71837_clk_probe(struct platform_device *pdev)
> > +{
> > +       struct bd71837_clk *c;
> > +       int rval = -ENOMEM;
> > +       struct bd71837 *mfd = dev_get_drvdata(pdev->dev.parent);
> > +       struct clk_init_data init = {
> > +               .name = "bd71837-32k-out",
> > +               .ops = &bd71837_clk_ops,
> > +       };
> > +
> > +       c = devm_kzalloc(&pdev->dev, sizeof(*c), GFP_KERNEL);
> > +       if (!c)
> > +               goto err_out;
> > +
> > +       c->reg = BD71837_REG_OUT32K;
> > +       c->mask = BD71837_OUT32K_EN;
> > +       c->rate = BD71837_CLK_RATE;
> 
> The PMIC has an 'XIN' pin that would be the clk input for this chip, and
> the output clk, this driver, would specify that xin clock as the parent.
> The 'xin' clock would then be listed in DT as a fixed-rate clock. That
> way this driver doesn't hardcode the frequency.

I see. This makes sense. I need to verify from HW colleagues whether
this chip has internal oscillator or not. I originally thought we have
on-chip oscillator - but as you say, we do have XIN pin in documentation.
So now I am not sure if the test board I have contains oscillator driving
the clk on PMIC - or if the PMIC has internal oscillator. I'll clarify this.

> > +       c->mfd = mfd;
> > +       c->pdev = pdev;
> > +
> > +       of_property_read_string_index(pdev->dev.parent->of_node,
> > +                                             "clock-output-names", 0,
> > +                                             &init.name);
> > +
> > +       c->hw.init = &init;
> 
> Do this next to all the other c-> things?

I can do so. I just like the idea of assigning pointers to objects only
after the objects have been initialized. Eg, in this case I add pointer
to init only after I have filled the init.name (if it is given).

> 
> > +
> > +       rval = devm_clk_hw_register(&pdev->dev, &c->hw);
> > +       if (rval) {
> > +               dev_err(&pdev->dev, "failed to register 32K clk");
> > +               goto err_out;
> > +       }
> > +
> > +       if (pdev->dev.parent->of_node) {
> > +               rval = of_clk_add_hw_provider(pdev->dev.parent->of_node,
> > +                                            of_clk_hw_simple_get,
> > +                                            &c->hw);
> > +               if (rval) {
> > +                       dev_err(&pdev->dev, "adding clk provider failed\n");
> > +                       goto err_out;
> 
> Just return rval instead of goto and then remove err_out label.

Is this 'hard requirement'? I prefer single point of exit from functions
(when easily doable) because I have done so many errors with locks /
resources being reserved when exiting from some error branch. For my
personal taste maintaining the one nice cleanup sequence is easier. But
if this is 'hard requirement' this can of course be changed.

> 
> > +               }
> > +       }
> > +
> > +       rval = clk_hw_register_clkdev(&c->hw, init.name, NULL);
> 
> This leaks a clkdev lookup. Sad we don't have a devm version. Maybe it
> should be added. But I really doubt this chip will be used with clkdev
> lookups so please remove clkdev until you have a user who needs it.

Yep. It leaks. I tried to look for an example where the clkdev was
nicely freed at remove - but I didn't find any. Every example I spotted
did leak the clkdev in same way as this does :( And I agree, devm
variant for freeing would be nice - or freeing routines based on hw.

As the leaked clkdev can cause oops at module unload/reload/use your
suggestion about removing it for now makes sense. I'll drop it.

> 
> > +       if (rval) {
> > +               dev_err(&pdev->dev, "failed to register clkdev for bd71837");
> > +               goto err_clean_provider;
> > +       }
> > +
> > +       platform_set_drvdata(pdev, c);
> > +
> > +       return 0;
> > +
> > +err_clean_provider:
> > +       of_clk_del_provider(pdev->dev.parent->of_node);
> > +err_out:
> > +       return rval;
> > +}
> > +
> > +static int bd71837_clk_remove(struct platform_device *pdev)
> > +{
> > +       if (pdev->dev.parent->of_node)
> > +               of_clk_del_provider(pdev->dev.parent->of_node);
> 
> Use devm so this can go away. Or is devm not used because the parent
> of_node is the provider? That's annoying.

What would be the correct workaround for this?

> 
> > +       return 0;
> > +}
> > +

Br,
    Matti Vaittinen

--
To unsubscribe from this list: send the line "unsubscribe linux-clk" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Matti Vaittinen June 13, 2018, 1:03 p.m. UTC | #3
On Tue, Jun 12, 2018 at 11:23:54AM +0300, Matti Vaittinen wrote:
> Hello Stephen,
> 
> Thanks again for the review. I'll do new patch which fixes these issues.
> 
> On Tue, Jun 12, 2018 at 12:44:11AM -0700, Stephen Boyd wrote:
> > Quoting Matti Vaittinen (2018-06-04 06:19:13)
> > > +}
> > > +
> > > +static unsigned long bd71837_clk_recalc_rate(struct clk_hw *hw,
> > > +                                            unsigned long parent_rate)
> > > +{
> > > +       struct bd71837_clk *c = container_of(hw, struct bd71837_clk, hw);
> > > +
> > > +       return c->rate;
> > > +}
> > > +
> > > +static const struct clk_ops bd71837_clk_ops = {
> > > +       .recalc_rate = &bd71837_clk_recalc_rate,
> > > +       .prepare = &bd71837_clk_enable,
> > > +       .unprepare = &bd71837_clk_disable,
> > > +       .is_prepared = &bd71837_clk_is_enabled,
> > > +};
> > > +
> > > +static int bd71837_clk_probe(struct platform_device *pdev)
> > > +{
> > > +       struct bd71837_clk *c;
> > > +       int rval = -ENOMEM;
> > > +       struct bd71837 *mfd = dev_get_drvdata(pdev->dev.parent);
> > > +       struct clk_init_data init = {
> > > +               .name = "bd71837-32k-out",
> > > +               .ops = &bd71837_clk_ops,
> > > +       };
> > > +
> > > +       c = devm_kzalloc(&pdev->dev, sizeof(*c), GFP_KERNEL);
> > > +       if (!c)
> > > +               goto err_out;
> > > +
> > > +       c->reg = BD71837_REG_OUT32K;
> > > +       c->mask = BD71837_OUT32K_EN;
> > > +       c->rate = BD71837_CLK_RATE;
> > 
> > The PMIC has an 'XIN' pin that would be the clk input for this chip, and
> > the output clk, this driver, would specify that xin clock as the parent.
> > The 'xin' clock would then be listed in DT as a fixed-rate clock. That
> > way this driver doesn't hardcode the frequency.
> 
> I see. This makes sense. I need to verify from HW colleagues whether
> this chip has internal oscillator or not. I originally thought we have
> on-chip oscillator - but as you say, we do have XIN pin in documentation.
> So now I am not sure if the test board I have contains oscillator driving
> the clk on PMIC - or if the PMIC has internal oscillator. I'll clarify this.

It really turned out that the PMIC just acts as a clock buffer. So I do
as you suggested and add lookup for parent clock to the driver. I
planned to do it so that if no parent is found from DT - then we assume
the 32.768KHz clock (as described in documentation). Eg, something along
the lines:

        init.parent_names = of_clk_get_parent_name(pdev->dev.parent->of_node, 0);
        if (init.parent_names) {
                init.num_parents = 1;
        } else {
                /* If parent is not given from DT we assume the typical use-case with
                 * 32.768 KHz oscillator for RTC (Maybe we could just error out here?)
                 */
                c->rate = BD71837_CLK_RATE;
                bd71837_clk_ops.recalc_rate = &bd71837_clk_recalc_rate;
        }

Does this make sense?

Br,
    Matti Vaittinen 
--
To unsubscribe from this list: send the line "unsubscribe linux-clk" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Stephen Boyd June 25, 2018, 11:44 p.m. UTC | #4
Quoting Matti Vaittinen (2018-06-12 01:23:54)
> On Tue, Jun 12, 2018 at 12:44:11AM -0700, Stephen Boyd wrote:
> > Quoting Matti Vaittinen (2018-06-04 06:19:13)
> 
> > 
> > > +struct bd71837_clk {
> > > +       struct clk_hw hw;
> > > +       uint8_t reg;
> > > +       uint8_t mask;
> > 
> > Use u8 instead of uint8_t.
> I can do that but I am afraid I have missed the reason for this. Why u8
> is preferred? I would have assumed the standard uint8_t would be
> preferred - can you please educate me as to what is this reason?

This is kernel style to prefer the shorter types within the kernel code.
Outside of the kernel proper, uint*_t types are used in the UAPI
headers. You can look through the mailing list about this, but this is
how I've known it to be for a while now.

> 
> > > +       unsigned long rate;
> > > +       struct platform_device *pdev;
> > > +       struct bd71837 *mfd;
> > > +};
> > > +
> > > +static int bd71837_clk_set(struct clk_hw *hw, int status)
> > > +{
> > > +       struct bd71837_clk *c = container_of(hw, struct bd71837_clk, hw);
> > > +
> > > +       return bd71837_update_bits(c->mfd, c->reg, c->mask, status);
> > 
> > Any reason we can't use a regmap?
> 
> Not really. These wrappers might become handy if the next chip in series
> requires some quirks for access but at the moment I see no reason for
> the wrappers. I could switch to direct regmap calls but that change
> should then be done to all subdevices and not just for clk one. This
> means I should rework also the already applied regulator part. I have
> some other changes pending for regulators (adding support for next chip,
> renaming bd71837 to bd718x7 or bd718xx, and few comments from  Andy
> Shevchenko and Rob Herring). I would rather switch to direct regmap usage
> for all subdevices at the same time. So if it is acceptable to keep the
> wrappers for now (and then create own patch set to remove them from all
> subdevices and the header) I would like to do so.

Ok. Sounds fine.

> 
> > 
> > > +}
> > > +
> > > +static void bd71837_clk_disable(struct clk_hw *hw)
> > > +{
> > > +       int rv;
> > > +       struct bd71837_clk *c = container_of(hw, struct bd71837_clk, hw);
> > > +
> > > +       rv = bd71837_clk_set(hw, 0);
> > > +       if (rv)
> > > +               dev_dbg(&c->pdev->dev, "Failed to disable 32K clk (%d)\n", rv);
> > > +}
> > > +
> > > +static int bd71837_clk_enable(struct clk_hw *hw)
> > > +{
> > > +       return bd71837_clk_set(hw, 1);
> > > +}
> > > +
> > > +static int bd71837_clk_is_enabled(struct clk_hw *hw)
> > > +{
> > > +       struct bd71837_clk *c = container_of(hw, struct bd71837_clk, hw);
> > > +
> > > +       return c->mask & bd71837_reg_read(c->mfd, c->reg);
> > 
> > Didn't I ask for local variable for reg_read result?
> 
> Yes! Sorry! And thanks for pointing this out again. It seems I missed
> this one.

Ok no worries.

> 
> > 
> > > +}
> > > +
> > > +static unsigned long bd71837_clk_recalc_rate(struct clk_hw *hw,
> > > +                                            unsigned long parent_rate)
> > > +{
> > > +       struct bd71837_clk *c = container_of(hw, struct bd71837_clk, hw);
> > > +
> > > +       return c->rate;
> > > +}
> > > +
> > > +static const struct clk_ops bd71837_clk_ops = {
> > > +       .recalc_rate = &bd71837_clk_recalc_rate,
> > > +       .prepare = &bd71837_clk_enable,
> > > +       .unprepare = &bd71837_clk_disable,
> > > +       .is_prepared = &bd71837_clk_is_enabled,
> > > +};
> > > +
> > > +static int bd71837_clk_probe(struct platform_device *pdev)
> > > +{
> > > +       struct bd71837_clk *c;
> > > +       int rval = -ENOMEM;
> > > +       struct bd71837 *mfd = dev_get_drvdata(pdev->dev.parent);
> > > +       struct clk_init_data init = {
> > > +               .name = "bd71837-32k-out",
> > > +               .ops = &bd71837_clk_ops,
> > > +       };
> > > +
> > > +       c = devm_kzalloc(&pdev->dev, sizeof(*c), GFP_KERNEL);
> > > +       if (!c)
> > > +               goto err_out;
> > > +
> > > +       c->reg = BD71837_REG_OUT32K;
> > > +       c->mask = BD71837_OUT32K_EN;
> > > +       c->rate = BD71837_CLK_RATE;
> > 
> > The PMIC has an 'XIN' pin that would be the clk input for this chip, and
> > the output clk, this driver, would specify that xin clock as the parent.
> > The 'xin' clock would then be listed in DT as a fixed-rate clock. That
> > way this driver doesn't hardcode the frequency.
> 
> I see. This makes sense. I need to verify from HW colleagues whether
> this chip has internal oscillator or not. I originally thought we have
> on-chip oscillator - but as you say, we do have XIN pin in documentation.
> So now I am not sure if the test board I have contains oscillator driving
> the clk on PMIC - or if the PMIC has internal oscillator. I'll clarify this.

Alright.

> 
> > > +       c->mfd = mfd;
> > > +       c->pdev = pdev;
> > > +
> > > +       of_property_read_string_index(pdev->dev.parent->of_node,
> > > +                                             "clock-output-names", 0,
> > > +                                             &init.name);
> > > +
> > > +       c->hw.init = &init;
> > 
> > Do this next to all the other c-> things?
> 
> I can do so. I just like the idea of assigning pointers to objects only
> after the objects have been initialized. Eg, in this case I add pointer
> to init only after I have filled the init.name (if it is given).

Alright, sure.

> 
> > 
> > > +
> > > +       rval = devm_clk_hw_register(&pdev->dev, &c->hw);
> > > +       if (rval) {
> > > +               dev_err(&pdev->dev, "failed to register 32K clk");
> > > +               goto err_out;
> > > +       }
> > > +
> > > +       if (pdev->dev.parent->of_node) {
> > > +               rval = of_clk_add_hw_provider(pdev->dev.parent->of_node,
> > > +                                            of_clk_hw_simple_get,
> > > +                                            &c->hw);
> > > +               if (rval) {
> > > +                       dev_err(&pdev->dev, "adding clk provider failed\n");
> > > +                       goto err_out;
> > 
> > Just return rval instead of goto and then remove err_out label.
> 
> Is this 'hard requirement'? I prefer single point of exit from functions
> (when easily doable) because I have done so many errors with locks /
> resources being reserved when exiting from some error branch. For my
> personal taste maintaining the one nice cleanup sequence is easier. But
> if this is 'hard requirement' this can of course be changed.

There are no locks though. And this uses devm. So please remove the goto
so my style alarms don't go off. Thanks!

> 
> > 
> > > +               }
> > > +       }
> > > +
> > > +       rval = clk_hw_register_clkdev(&c->hw, init.name, NULL);
> > 
> > This leaks a clkdev lookup. Sad we don't have a devm version. Maybe it
> > should be added. But I really doubt this chip will be used with clkdev
> > lookups so please remove clkdev until you have a user who needs it.
> 
> Yep. It leaks. I tried to look for an example where the clkdev was
> nicely freed at remove - but I didn't find any. Every example I spotted
> did leak the clkdev in same way as this does :( And I agree, devm
> variant for freeing would be nice - or freeing routines based on hw.
> 
> As the leaked clkdev can cause oops at module unload/reload/use your
> suggestion about removing it for now makes sense. I'll drop it.

Ok! Sometimes drivers don't free them because they're lazy and don't
expect to be unloaded or to fail. I guess you ran into those. I'm happy
to apply patches to clean those up.

> 
> > 
> > > +       if (rval) {
> > > +               dev_err(&pdev->dev, "failed to register clkdev for bd71837");
> > > +               goto err_clean_provider;
> > > +       }
> > > +
> > > +       platform_set_drvdata(pdev, c);
> > > +
> > > +       return 0;
> > > +
> > > +err_clean_provider:
> > > +       of_clk_del_provider(pdev->dev.parent->of_node);
> > > +err_out:
> > > +       return rval;
> > > +}
> > > +
> > > +static int bd71837_clk_remove(struct platform_device *pdev)
> > > +{
> > > +       if (pdev->dev.parent->of_node)
> > > +               of_clk_del_provider(pdev->dev.parent->of_node);
> > 
> > Use devm so this can go away. Or is devm not used because the parent
> > of_node is the provider? That's annoying.
> 
> What would be the correct workaround for this?

Smash the clk driver into the overall PMIC node. That should work. Or
possibly assign the same of_node to the child device when creating it?
I'm not sure if that causes some sort of problem with DT code though, so
it would be good to check with Rob H if that's a bad idea or not.

--
To unsubscribe from this list: send the line "unsubscribe linux-clk" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Stephen Boyd June 25, 2018, 11:46 p.m. UTC | #5
Quoting Matti Vaittinen (2018-06-13 06:03:38)
> On Tue, Jun 12, 2018 at 11:23:54AM +0300, Matti Vaittinen wrote:
> > 
> > I see. This makes sense. I need to verify from HW colleagues whether
> > this chip has internal oscillator or not. I originally thought we have
> > on-chip oscillator - but as you say, we do have XIN pin in documentation.
> > So now I am not sure if the test board I have contains oscillator driving
> > the clk on PMIC - or if the PMIC has internal oscillator. I'll clarify this.
> 
> It really turned out that the PMIC just acts as a clock buffer. So I do
> as you suggested and add lookup for parent clock to the driver. I
> planned to do it so that if no parent is found from DT - then we assume
> the 32.768KHz clock (as described in documentation). Eg, something along
> the lines:
> 
>         init.parent_names = of_clk_get_parent_name(pdev->dev.parent->of_node, 0);
>         if (init.parent_names) {
>                 init.num_parents = 1;
>         } else {
>                 /* If parent is not given from DT we assume the typical use-case with
>                  * 32.768 KHz oscillator for RTC (Maybe we could just error out here?)
>                  */
>                 c->rate = BD71837_CLK_RATE;
>                 bd71837_clk_ops.recalc_rate = &bd71837_clk_recalc_rate;
>         }

You can also add a clk directly in this driver in that case there isn't
one in DT with the rate and name of your choosing. Then the logic is the
same and we don't need a c->rate variable.

--
To unsubscribe from this list: send the line "unsubscribe linux-clk" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Vaittinen, Matti June 26, 2018, 8:13 a.m. UTC | #6
Hello Stephen, Rob and all others

Thanks again Stephen. I appreciate your help. And just to help you
sorting your inbox a bit - I have written patch series v6 and v7 before
receiving these comments. I will write v8 after I get some further input
from you and Rob on how to solve the issue with having the clk stuff in
parent node and usage of devm_of_clk_add_hw_provider where DT node is
fetched from dev pointer. 

The v8 should then address rest of the issues - except the regmap wrappers
which I will send as separate patch. So I guess you can skip the v6 and v7
and just please check for the v8 when I get it done.

On Mon, Jun 25, 2018 at 04:44:57PM -0700, Stephen Boyd wrote:
> Quoting Matti Vaittinen (2018-06-12 01:23:54)
> > On Tue, Jun 12, 2018 at 12:44:11AM -0700, Stephen Boyd wrote:
> > > Quoting Matti Vaittinen (2018-06-04 06:19:13)
> > > Use u8 instead of uint8_t.
> > can you please educate me as to what is this reason?
> 
> This is kernel style to prefer the shorter types within the kernel code.
> Outside of the kernel proper, uint*_t types are used in the UAPI
> headers. You can look through the mailing list about this, but this is
> how I've known it to be for a while now.

Thanks. I have changed this from patch v6 onwards.
> > > > +
> > > > +       rval = devm_clk_hw_register(&pdev->dev, &c->hw);
> > > > +       if (rval) {
> > > > +               dev_err(&pdev->dev, "failed to register 32K clk");
> > > > +               goto err_out;
> > > > +       }
> > > > +
> > > > +       if (pdev->dev.parent->of_node) {
> > > > +               rval = of_clk_add_hw_provider(pdev->dev.parent->of_node,
> > > > +                                            of_clk_hw_simple_get,
> > > > +                                            &c->hw);
> > > > +               if (rval) {
> > > > +                       dev_err(&pdev->dev, "adding clk provider failed\n");
> > > > +                       goto err_out;
> > > 
> > > Just return rval instead of goto and then remove err_out label.
> > 
> > Is this 'hard requirement'? I prefer single point of exit from functions
> > (when easily doable) because I have done so many errors with locks /
> > resources being reserved when exiting from some error branch. For my
> > personal taste maintaining the one nice cleanup sequence is easier. But
> > if this is 'hard requirement' this can of course be changed.
> 
> There are no locks though. And this uses devm. So please remove the goto
> so my style alarms don't go off. Thanks!

I did rework this piece from patch v6 onwards so that there is no goto but
we still have single exit point. I hope that is Ok, so please re-check
this when you find the time.

> > > > +       rval = clk_hw_register_clkdev(&c->hw, init.name, NULL);
> > > 
> > > This leaks a clkdev lookup. Sad we don't have a devm version. Maybe it
> > > should be added. But I really doubt this chip will be used with clkdev
> > > lookups so please remove clkdev until you have a user who needs it.
> > 
> > Yep. It leaks. I tried to look for an example where the clkdev was
> > nicely freed at remove - but I didn't find any. Every example I spotted
> > did leak the clkdev in same way as this does :( And I agree, devm
> > variant for freeing would be nice - or freeing routines based on hw.
> > 
> > As the leaked clkdev can cause oops at module unload/reload/use your
> > suggestion about removing it for now makes sense. I'll drop it.
> 
> Ok! Sometimes drivers don't free them because they're lazy and don't
> expect to be unloaded or to fail. I guess you ran into those. I'm happy
> to apply patches to clean those up.

When I get the moment of "hmm, what should I do next" - I'll keep this
in mind... Unfortunately those moments have been pretty rare occasions
since I found my wife and got my kids - but OTOH, we have working
pension scheme in Finland - so I expect this to change during the next
30 years or so =] (Truth is that I'll keep this in mind but can't
promise anything more as promises might be empty).

> > > > +       if (rval) {
> > > > +               dev_err(&pdev->dev, "failed to register clkdev for bd71837");
> > > > +               goto err_clean_provider;
> > > > +       }
> > > > +
> > > > +       platform_set_drvdata(pdev, c);
> > > > +
> > > > +       return 0;
> > > > +
> > > > +err_clean_provider:
> > > > +       of_clk_del_provider(pdev->dev.parent->of_node);
> > > > +err_out:
> > > > +       return rval;
> > > > +}
> > > > +
> > > > +static int bd71837_clk_remove(struct platform_device *pdev)
> > > > +{
> > > > +       if (pdev->dev.parent->of_node)
> > > > +               of_clk_del_provider(pdev->dev.parent->of_node);
> > > 
> > > Use devm so this can go away. Or is devm not used because the parent
> > > of_node is the provider? That's annoying.
> > 
> > What would be the correct workaround for this?
> 
> Smash the clk driver into the overall PMIC node. That should work. Or
> possibly assign the same of_node to the child device when creating it?
> I'm not sure if that causes some sort of problem with DT code though, so
> it would be good to check with Rob H if that's a bad idea or not.

I'd rather keep the clk in own subdevice so that it can be used or not
used based on the clk Kconfig options. I also rather keep the clk codes
in clk folders. So I guess the use of devm is not correct justification
for bundling the MFD and clk. I basically see 3 ways:

1. Assign MFD node to subdevice node in MFD when creating the cells.
2. Assign parent->of_node to dev.of_node in clk subdevice.
3. Create devm_of_clk_add_hw_provider_w_node() which does something
like (not compiled pseudo) code below

int devm_of_clk_add_hw_provider_w_node(struct device *dev,
			struct clk_hw *(*get)(struct of_phandle_args *clkspec,
					      void *data),
			struct device_node *of_node,
			void *data)
{
	struct device_node **ptr;
	int ret;
	ptr = devres_alloc(devm_of_clk_release_provider, sizeof(*ptr),
			   GFP_KERNEL);
	if (!ptr)
		return -ENOMEM;

	*ptr = of_node;
	ret = of_clk_add_hw_provider(of_node, get, data);
	if (!ret)
		devres_add(dev, ptr);
	else
		devres_free(ptr);

	return ret;
}
EXPORT_SYMBOL_GPL(devm_of_clk_add_hw_provider_w_node);

int devm_of_clk_add_hw_provider(struct device *dev,
			struct clk_hw *(*get)(struct of_phandle_args *clkspec,
					      void *data),
			void *data)
{
	return devm_of_clk_add_hw_provider_w_node(dev, get, dev->of_node,
						  data);
}
EXPORT_SYMBOL_GPL(devm_of_clk_add_hw_provider);

To me the third option sounds like correct one - but you guys probably
have the idea how these subsystems should look like in the future - so I
trust on your judgement on this. So whatäs your take on this?

I'll also add Rob in the 'to' field of this email so maybe we get his opinion
on this.

Best Regards
    Matti Vaittinen

--
To unsubscribe from this list: send the line "unsubscribe linux-clk" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Vaittinen, Matti June 27, 2018, 8:40 a.m. UTC | #7
Hello Stephen,

On Mon, Jun 25, 2018 at 04:46:24PM -0700, Stephen Boyd wrote:
> Quoting Matti Vaittinen (2018-06-13 06:03:38)
> > On Tue, Jun 12, 2018 at 11:23:54AM +0300, Matti Vaittinen wrote:
> > > 
> > > I see. This makes sense. I need to verify from HW colleagues whether
> > > this chip has internal oscillator or not. I originally thought we have
> > > on-chip oscillator - but as you say, we do have XIN pin in documentation.
> > > So now I am not sure if the test board I have contains oscillator driving
> > > the clk on PMIC - or if the PMIC has internal oscillator. I'll clarify this.
> > 
> > It really turned out that the PMIC just acts as a clock buffer. So I do
> > as you suggested and add lookup for parent clock to the driver. I
> > planned to do it so that if no parent is found from DT - then we assume
> > the 32.768KHz clock (as described in documentation). Eg, something along
> > the lines:
> > 
> >         init.parent_names = of_clk_get_parent_name(pdev->dev.parent->of_node, 0);
> >         if (init.parent_names) {
> >                 init.num_parents = 1;
> >         } else {
> >                 /* If parent is not given from DT we assume the typical use-case with
> >                  * 32.768 KHz oscillator for RTC (Maybe we could just error out here?)
> >                  */
> >                 c->rate = BD71837_CLK_RATE;
> >                 bd71837_clk_ops.recalc_rate = &bd71837_clk_recalc_rate;
> >         }
> 
> You can also add a clk directly in this driver in that case there isn't
> one in DT with the rate and name of your choosing. Then the logic is the
> same and we don't need a c->rate variable.

So you mean that I should use clk_hw_register_fixed_rate and create new
clk if parent is not found? Isn't this a bit of an overkill? Downside is
that then we do need remove/cleanup functionality for deleting this
parent clock - and I didn't find devm support for fixed clock. Furthermore
I guess that since it is parent, it can't be removed before child is removed.

Or did you mean something else but creating a fixed rate clock as parent
here?

Best Regards
    Matti Vaittinen

--
To unsubscribe from this list: send the line "unsubscribe linux-clk" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Vaittinen, Matti June 29, 2018, 8:34 a.m. UTC | #8
On Tue, Jun 26, 2018 at 11:13:19AM +0300, Matti Vaittinen wrote:
> Hello Stephen, Rob and all others
> 
> Thanks again Stephen. I appreciate your help. And just to help you
> sorting your inbox a bit - I have written patch series v6 and v7 before
> receiving these comments. I will write v8 after I get some further input
> from you and Rob on how to solve the issue with having the clk stuff in
> parent node and usage of devm_of_clk_add_hw_provider where DT node is
> fetched from dev pointer. 

Just for your information - I did send v8 without clk part as I was
unsure how to proceed with it. I still wanted to get the MFD fixes sent
before I start my vacations and family time. I will wait for input from
you / Rob and continue the clk portion latest after 4 weeks. Sorry for
long delay =(

Have a nice Summer
-- Matti Vaittinen
--
To unsubscribe from this list: send the line "unsubscribe linux-clk" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Vaittinen, Matti July 31, 2018, 8:28 a.m. UTC | #9
On Tue, Jun 26, 2018 at 11:13:19AM +0300, Matti Vaittinen wrote:
> On Mon, Jun 25, 2018 at 04:44:57PM -0700, Stephen Boyd wrote:
> > Quoting Matti Vaittinen (2018-06-12 01:23:54)
> > > On Tue, Jun 12, 2018 at 12:44:11AM -0700, Stephen Boyd wrote:
> > > > Quoting Matti Vaittinen (2018-06-04 06:19:13)

[snip]

> > > > > +       if (rval) {
> > > > > +               dev_err(&pdev->dev, "failed to register clkdev for bd71837");
> > > > > +               goto err_clean_provider;
> > > > > +       }
> > > > > +
> > > > > +       platform_set_drvdata(pdev, c);
> > > > > +
> > > > > +       return 0;
> > > > > +
> > > > > +err_clean_provider:
> > > > > +       of_clk_del_provider(pdev->dev.parent->of_node);
> > > > > +err_out:
> > > > > +       return rval;
> > > > > +}
> > > > > +
> > > > > +static int bd71837_clk_remove(struct platform_device *pdev)
> > > > > +{
> > > > > +       if (pdev->dev.parent->of_node)
> > > > > +               of_clk_del_provider(pdev->dev.parent->of_node);
> > > > 
> > > > Use devm so this can go away. Or is devm not used because the parent
> > > > of_node is the provider? That's annoying.
> > > 
> > > What would be the correct workaround for this?
> > 
> > Smash the clk driver into the overall PMIC node. That should work. Or
> > possibly assign the same of_node to the child device when creating it?
> > I'm not sure if that causes some sort of problem with DT code though, so
> > it would be good to check with Rob H if that's a bad idea or not.
> 
> 1. Assign MFD node to subdevice node in MFD when creating the cells.
> 2. Assign parent->of_node to dev.of_node in clk subdevice.
> 3. Create devm_of_clk_add_hw_provider_w_node() which does something
> like (not compiled pseudo) code below
> 
> int devm_of_clk_add_hw_provider_w_node(struct device *dev,
> 			struct clk_hw *(*get)(struct of_phandle_args *clkspec,
> 					      void *data),
> 			struct device_node *of_node,
> 			void *data)
> {
> 	struct device_node **ptr;
> 	int ret;
> 	ptr = devres_alloc(devm_of_clk_release_provider, sizeof(*ptr),
> 			   GFP_KERNEL);
> 	if (!ptr)
> 		return -ENOMEM;
> 
> 	*ptr = of_node;
> 	ret = of_clk_add_hw_provider(of_node, get, data);
> 	if (!ret)
> 		devres_add(dev, ptr);
> 	else
> 		devres_free(ptr);
> 
> 	return ret;
> }
> EXPORT_SYMBOL_GPL(devm_of_clk_add_hw_provider_w_node);
> 
> int devm_of_clk_add_hw_provider(struct device *dev,
> 			struct clk_hw *(*get)(struct of_phandle_args *clkspec,
> 					      void *data),
> 			void *data)
> {
> 	return devm_of_clk_add_hw_provider_w_node(dev, get, dev->of_node,
> 						  data);
> }
> EXPORT_SYMBOL_GPL(devm_of_clk_add_hw_provider);

just a friendly reminder, what's your opinion on adding this kind of
function (devm_of_clk_add_hw_provider_w_node)? or solutions 1/2? And are these options safe what comes to
reference counting of of_nodes?

Best regards
	Matti Vaittinen
--
To unsubscribe from this list: send the line "unsubscribe linux-clk" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Vaittinen, Matti July 31, 2018, 9:05 a.m. UTC | #10
Hello Again,

On Wed, Jun 27, 2018 at 11:40:00AM +0300, Matti Vaittinen wrote:
> Hello Stephen,
> 
> On Mon, Jun 25, 2018 at 04:46:24PM -0700, Stephen Boyd wrote:
> > Quoting Matti Vaittinen (2018-06-13 06:03:38)
> > > On Tue, Jun 12, 2018 at 11:23:54AM +0300, Matti Vaittinen wrote:
> > > > 
> > > > I see. This makes sense. I need to verify from HW colleagues whether
> > > > this chip has internal oscillator or not. I originally thought we have
> > > > on-chip oscillator - but as you say, we do have XIN pin in documentation.
> > > > So now I am not sure if the test board I have contains oscillator driving
> > > > the clk on PMIC - or if the PMIC has internal oscillator. I'll clarify this.
> > > 
> > > It really turned out that the PMIC just acts as a clock buffer. So I do
> > > as you suggested and add lookup for parent clock to the driver. I
> > > planned to do it so that if no parent is found from DT - then we assume
> > > the 32.768KHz clock (as described in documentation). Eg, something along
> > > the lines:
> > > 
> > >         init.parent_names = of_clk_get_parent_name(pdev->dev.parent->of_node, 0);
> > >         if (init.parent_names) {
> > >                 init.num_parents = 1;
> > >         } else {
> > >                 /* If parent is not given from DT we assume the typical use-case with
> > >                  * 32.768 KHz oscillator for RTC (Maybe we could just error out here?)
> > >                  */
> > >                 c->rate = BD71837_CLK_RATE;
> > >                 bd71837_clk_ops.recalc_rate = &bd71837_clk_recalc_rate;
> > >         }
> > 
> > You can also add a clk directly in this driver in that case there isn't
> > one in DT with the rate and name of your choosing. Then the logic is the
> > same and we don't need a c->rate variable.
> 
> So you mean that I should use clk_hw_register_fixed_rate and create new
> clk if parent is not found? Isn't this a bit of an overkill? Downside is
> that then we do need remove/cleanup functionality for deleting this
> parent clock - and I didn't find devm support for fixed clock. Furthermore
> I guess that since it is parent, it can't be removed before child is removed.
> 
> Or did you mean something else but creating a fixed rate clock as parent
> here?

I would be grateful for any tips on how to proceed. Should I

1. Really create a fixed rate clock - and build cleanup sequence myself
2. Keep this simple and fail if no parent is found using of_clk_get_parent_name

Best regards
    Matti Vaittinen
--
To unsubscribe from this list: send the line "unsubscribe linux-clk" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Vaittinen, Matti Aug. 3, 2018, 8:09 a.m. UTC | #11
On Tue, Jul 31, 2018 at 11:28:58AM +0300, Matti Vaittinen wrote:
> On Tue, Jun 26, 2018 at 11:13:19AM +0300, Matti Vaittinen wrote:
> > On Mon, Jun 25, 2018 at 04:44:57PM -0700, Stephen Boyd wrote:
> > > Quoting Matti Vaittinen (2018-06-12 01:23:54)
> > > > On Tue, Jun 12, 2018 at 12:44:11AM -0700, Stephen Boyd wrote:
> > > > > Quoting Matti Vaittinen (2018-06-04 06:19:13)

[snip]

> 
> > 3. Create devm_of_clk_add_hw_provider_w_node() which does something
> 

After giving this second thought - I think there is limited amount of
use cases where other than own or parent nodes should be used. Actually,
the MFD node being parent is pretty much only use case I can think of
where something else but own node should be used. Hence function like
suggested devm_of_clk_add_hw_provider_w_node might invite thinking of
clever hacks... So, perhaps introducing
devm_of_clk_add_hw_provider_parent() (see idea below) would be option to
consider? I feel the bd71837 driver is not only case where MFD is being
parent which has the clock stuff in DT.

static int __devm_of_clk_add_hw_provider(struct device *dev,
	struct clk_hw *(*get)(struct of_phandle_args *clkspec, void *data),
	struct device_node *of_node, void *data)
{
	struct device_node **ptr;
	int ret;
	ptr = devres_alloc(devm_of_clk_release_provider, sizeof(*ptr),
			   GFP_KERNEL);
	if (!ptr)
		return -ENOMEM;

	*ptr = of_node;
	ret = of_clk_add_hw_provider(of_node, get, data);
	if (!ret)
		devres_add(dev, ptr);
	else
		devres_free(ptr);
	return ret;
}

int devm_of_clk_add_hw_provider(struct device *dev,
	struct clk_hw *(*get)(struct of_phandle_args *clkspec, void *data),
	void *data)
{
	return __devm_of_clk_add_hw_provider(dev, get, dev->of_node, data);
}
EXPORT_SYMBOL_GPL(devm_of_clk_add_hw_provider);

int devm_of_clk_add_hw_provider_parent(struct device *dev,
	struct clk_hw *(*get)(struct of_phandle_args *clkspec, void *data),
	void *data)
{
	return __devm_of_clk_add_hw_provider(*dev, get, dev->parent->of_node,
					     data);
}
EXPORT_SYMBOL_GPL(devm_of_clk_add_hw_provider_parent);

> just a friendly reminder, what's your opinion on adding this kind of
> function (devm_of_clk_add_hw_provider_w_node)? or solutions 1/2? And are
> these options safe what comes to reference counting of of_nodes?

I thik the reference counting should not be a problem when use is
limited to (MFD) parent device nodes, right?

Best regards
    Matti Vaittinen
--
To unsubscribe from this list: send the line "unsubscribe linux-clk" 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

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 41492e980ef4..e693496f202a 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -279,6 +279,13 @@  config COMMON_CLK_STM32H7
 	---help---
 	  Support for stm32h7 SoC family clocks
 
+config COMMON_CLK_BD71837
+	tristate "Clock driver for ROHM BD71837 PMIC MFD"
+	depends on MFD_BD71837
+	help
+	  This driver supports ROHM BD71837 PMIC clock.
+
+
 source "drivers/clk/bcm/Kconfig"
 source "drivers/clk/hisilicon/Kconfig"
 source "drivers/clk/imgtec/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index de6d06ac790b..8393c4af7d5a 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -21,6 +21,7 @@  endif
 obj-$(CONFIG_MACH_ASM9260)		+= clk-asm9260.o
 obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN)	+= clk-axi-clkgen.o
 obj-$(CONFIG_ARCH_AXXIA)		+= clk-axm5516.o
+obj-$(CONFIG_COMMON_CLK_BD71837)	+= clk-bd71837.o
 obj-$(CONFIG_COMMON_CLK_CDCE706)	+= clk-cdce706.o
 obj-$(CONFIG_COMMON_CLK_CDCE925)	+= clk-cdce925.o
 obj-$(CONFIG_ARCH_CLPS711X)		+= clk-clps711x.o
diff --git a/drivers/clk/clk-bd71837.c b/drivers/clk/clk-bd71837.c
new file mode 100644
index 000000000000..5ba6c05c5a98
--- /dev/null
+++ b/drivers/clk/clk-bd71837.c
@@ -0,0 +1,146 @@ 
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 ROHM Semiconductors
+// bd71837.c  -- ROHM BD71837MWV clock driver
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mfd/bd71837.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+
+
+struct bd71837_clk {
+	struct clk_hw hw;
+	uint8_t reg;
+	uint8_t mask;
+	unsigned long rate;
+	struct platform_device *pdev;
+	struct bd71837 *mfd;
+};
+
+static int bd71837_clk_set(struct clk_hw *hw, int status)
+{
+	struct bd71837_clk *c = container_of(hw, struct bd71837_clk, hw);
+
+	return bd71837_update_bits(c->mfd, c->reg, c->mask, status);
+}
+
+static void bd71837_clk_disable(struct clk_hw *hw)
+{
+	int rv;
+	struct bd71837_clk *c = container_of(hw, struct bd71837_clk, hw);
+
+	rv = bd71837_clk_set(hw, 0);
+	if (rv)
+		dev_dbg(&c->pdev->dev, "Failed to disable 32K clk (%d)\n", rv);
+}
+
+static int bd71837_clk_enable(struct clk_hw *hw)
+{
+	return bd71837_clk_set(hw, 1);
+}
+
+static int bd71837_clk_is_enabled(struct clk_hw *hw)
+{
+	struct bd71837_clk *c = container_of(hw, struct bd71837_clk, hw);
+
+	return c->mask & bd71837_reg_read(c->mfd, c->reg);
+}
+
+static unsigned long bd71837_clk_recalc_rate(struct clk_hw *hw,
+					     unsigned long parent_rate)
+{
+	struct bd71837_clk *c = container_of(hw, struct bd71837_clk, hw);
+
+	return c->rate;
+}
+
+static const struct clk_ops bd71837_clk_ops = {
+	.recalc_rate = &bd71837_clk_recalc_rate,
+	.prepare = &bd71837_clk_enable,
+	.unprepare = &bd71837_clk_disable,
+	.is_prepared = &bd71837_clk_is_enabled,
+};
+
+static int bd71837_clk_probe(struct platform_device *pdev)
+{
+	struct bd71837_clk *c;
+	int rval = -ENOMEM;
+	struct bd71837 *mfd = dev_get_drvdata(pdev->dev.parent);
+	struct clk_init_data init = {
+		.name = "bd71837-32k-out",
+		.ops = &bd71837_clk_ops,
+	};
+
+	c = devm_kzalloc(&pdev->dev, sizeof(*c), GFP_KERNEL);
+	if (!c)
+		goto err_out;
+
+	c->reg = BD71837_REG_OUT32K;
+	c->mask = BD71837_OUT32K_EN;
+	c->rate = BD71837_CLK_RATE;
+	c->mfd = mfd;
+	c->pdev = pdev;
+
+	of_property_read_string_index(pdev->dev.parent->of_node,
+					      "clock-output-names", 0,
+					      &init.name);
+
+	c->hw.init = &init;
+
+	rval = devm_clk_hw_register(&pdev->dev, &c->hw);
+	if (rval) {
+		dev_err(&pdev->dev, "failed to register 32K clk");
+		goto err_out;
+	}
+
+	if (pdev->dev.parent->of_node) {
+		rval = of_clk_add_hw_provider(pdev->dev.parent->of_node,
+					     of_clk_hw_simple_get,
+					     &c->hw);
+		if (rval) {
+			dev_err(&pdev->dev, "adding clk provider failed\n");
+			goto err_out;
+		}
+	}
+
+	rval = clk_hw_register_clkdev(&c->hw, init.name, NULL);
+	if (rval) {
+		dev_err(&pdev->dev, "failed to register clkdev for bd71837");
+		goto err_clean_provider;
+	}
+
+	platform_set_drvdata(pdev, c);
+
+	return 0;
+
+err_clean_provider:
+	of_clk_del_provider(pdev->dev.parent->of_node);
+err_out:
+	return rval;
+}
+
+static int bd71837_clk_remove(struct platform_device *pdev)
+{
+	if (pdev->dev.parent->of_node)
+		of_clk_del_provider(pdev->dev.parent->of_node);
+	return 0;
+}
+
+static struct platform_driver bd71837_clk = {
+	.driver = {
+		.name = "bd71837-clk",
+	},
+	.probe = bd71837_clk_probe,
+	.remove = bd71837_clk_remove,
+};
+
+module_platform_driver(bd71837_clk);
+
+MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
+MODULE_DESCRIPTION("BD71837 chip clk driver");
+MODULE_LICENSE("GPL");