Subject: [PATCH] CLK: clk-twl6040: Initial clock driver for OMAP4+ McPDM fclk clock

From: [email-masked] (Mike Turquette)
Date: Mon, 29 Oct 2012 10:30:08 -0700

Quoting Peter Ujfalusi (2012-09-14 07:30:27)
> On OMAP4+ platforms the functional clock for the McPDM IP is suplied by
> the twl6040 codec (bit clock on the PDM bus).
> This common clock driver for twl6040 will register the mcpdm_fclk clock to
> be used by the McPDM driver to make sure that the needed clocks are
> available when needed.
> 
> Signed-off-by: Peter Ujfalusi <[email-masked]>
> ---
> 
> Hello Mike,
> 
> This driver is going to be used by the OMAP McPDM driver when we moved to common
> clock framework.
> To avoid merge conflicts I'm going to send the patch needed for the twl6040 MFD
> core driver to register the platform device for the clk driver.
> 

Peter,

The patch looks good to me.  To be clear, you wan this to go through
clk-next or the mfd tree for avoiding conflicts?

Thanks,
Mike

> Some background: OMAP McPDM's functional clock is coming from external source,
> which is the bitclock of the McPDM interface generated by external codec
> (twl6040). This clock is needed to access McPDM registers as well and when I'm
> going to implement the power states of twl6040 we need to make sure that the
> clock is running from twl6040 to McPDM in order to avoid surprises.
> 
> Regards,
> Peter
> 
>  drivers/clk/Kconfig       |   8 +++
>  drivers/clk/Makefile      |   1 +
>  drivers/clk/clk-twl6040.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 135 insertions(+)
>  create mode 100644 drivers/clk/clk-twl6040.c
> 
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index bace9e9..3d0b784 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -53,4 +53,12 @@ config COMMON_CLK_MAX77686
>         ---help---
>           This driver supports Maxim 77686 crystal oscillator clock. 
>  
> +config CLK_TWL6040
> +       tristate "External McPDM functional clock from twl6040"
> +       depends on TWL6040_CORE
> +       ---help---
> +         Enable the external functional clock support on OMAP4+ platforms for
> +         McPDM. McPDM module is using the external bit clock on the McPDM bus
> +         as functional clock.
> +
>  endmenu
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index 6327536..c55d840 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -20,3 +20,4 @@ obj-$(CONFIG_ARCH_U8500)      += ux500/
>  # Chip specific
>  obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
>  obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
> +obj-$(CONFIG_CLK_TWL6040)      += clk-twl6040.o
> diff --git a/drivers/clk/clk-twl6040.c b/drivers/clk/clk-twl6040.c
> new file mode 100644
> index 0000000..f4a3389
> --- /dev/null
> +++ b/drivers/clk/clk-twl6040.c
> @@ -0,0 +1,126 @@
> +/*
> +* TWL6040 clock module driver for OMAP4 McPDM functional clock
> +*
> +* Copyright (C) 2012 Texas Instruments Inc.
> +* Peter Ujfalusi <[email-masked]>
> +*
> +* 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.
> +*
> +* This program is distributed in the hope that it will be useful, but
> +* WITHOUT ANY WARRANTY; without even the implied warranty of
> +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> +* General Public License for more details.
> +*
> +* You should have received a copy of the GNU General Public License
> +* along with this program; if not, write to the Free Software
> +* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> +* 02110-1301 USA
> +*
> +*/
> +
> +#include <linux/clk.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/platform_device.h>
> +#include <linux/mfd/twl6040.h>
> +#include <linux/clk-provider.h>
> +
> +struct twl6040_clk {
> +       struct twl6040 *twl6040;
> +       struct device *dev;
> +       struct clk_hw mcpdm_fclk;
> +       struct clk *clk;
> +       int enabled;
> +};
> +
> +static int twl6040_bitclk_is_enabled(struct clk_hw *hw)
> +{
> +       struct twl6040_clk *twl6040_clk = container_of(hw, struct twl6040_clk,
> +                                                      mcpdm_fclk);
> +       return twl6040_clk->enabled;
> +}
> +
> +static int twl6040_bitclk_prepare(struct clk_hw *hw)
> +{
> +       struct twl6040_clk *twl6040_clk = container_of(hw, struct twl6040_clk,
> +                                                      mcpdm_fclk);
> +       int ret;
> +
> +       ret = twl6040_power(twl6040_clk->twl6040, 1);
> +       if (!ret)
> +               twl6040_clk->enabled = 1;
> +
> +       return ret;
> +}
> +
> +static void twl6040_bitclk_unprepare(struct clk_hw *hw)
> +{
> +       struct twl6040_clk *twl6040_clk = container_of(hw, struct twl6040_clk,
> +                                                      mcpdm_fclk);
> +       int ret;
> +
> +       ret = twl6040_power(twl6040_clk->twl6040, 0);
> +       if (!ret)
> +               twl6040_clk->enabled = 0;
> +}
> +
> +static const struct clk_ops twl6040_mcpdm_ops = {
> +       .is_enabled = twl6040_bitclk_is_enabled,
> +       .prepare = twl6040_bitclk_prepare,
> +       .unprepare = twl6040_bitclk_unprepare,
> +};
> +
> +static struct clk_init_data wm831x_clkout_init = {
> +       .name = "mcpdm_fclk",
> +       .ops = &twl6040_mcpdm_ops,
> +       .flags = CLK_IS_ROOT,
> +};
> +
> +static int __devinit twl6040_clk_probe(struct platform_device *pdev)
> +{
> +       struct twl6040 *twl6040 = dev_get_drvdata(pdev->dev.parent);
> +       struct twl6040_clk *clkdata;
> +
> +       clkdata = devm_kzalloc(&pdev->dev, sizeof(*clkdata), GFP_KERNEL);
> +       if (!clkdata)
> +               return -ENOMEM;
> +
> +       clkdata->dev = &pdev->dev;
> +       clkdata->twl6040 = twl6040;
> +
> +       clkdata->mcpdm_fclk.init = &wm831x_clkout_init;
> +       clkdata->clk = clk_register(&pdev->dev, &clkdata->mcpdm_fclk);
> +       if (!clkdata->clk)
> +               return -EINVAL;
> +
> +       dev_set_drvdata(&pdev->dev, clkdata);
> +
> +       return 0;
> +}
> +
> +static int __devexit twl6040_clk_remove(struct platform_device *pdev)
> +{
> +       struct twl6040_clk *clkdata = dev_get_drvdata(&pdev->dev);
> +
> +       clk_unregister(clkdata->clk);
> +
> +       return 0;
> +}
> +
> +static struct platform_driver twl6040_clk_driver = {
> +       .driver = {
> +               .name = "twl6040-clk",
> +               .owner = THIS_MODULE,
> +       },
> +       .probe = twl6040_clk_probe,
> +       .remove = __devexit_p(twl6040_clk_remove),
> +};
> +
> +module_platform_driver(twl6040_clk_driver);
> +
> +MODULE_DESCRIPTION("TWL6040 clock driver for McPDM functional clock");
> +MODULE_AUTHOR("Peter Ujfalusi <[email-masked]>");
> +MODULE_ALIAS("platform:twl6040-clk");
> +MODULE_LICENSE("GPL");
> -- 
> 1.7.12