linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] clock: max77686: Add driver for Maxim 77686 32Khz crystal oscillator.
@ 2012-08-28  8:54 Jonghwa Lee
  2012-09-07  0:04 ` Mike Turquette
  0 siblings, 1 reply; 8+ messages in thread
From: Jonghwa Lee @ 2012-08-28  8:54 UTC (permalink / raw)
  To: linux-kernel
  Cc: Mike Turquette, Arnd Bergmann, H Hartley Sweeten, Jonghwa Lee

This patch supports max77686 mfd's clock driver using common clock frame work.
max77686 has 3 clock ouputs which all are generated from crystal oscillator and
SOC can enable/disable them via I2C bus. All clocks are fixed-rate clock sources
so that it doesn't supply interface for changing clock rate.
Driver uses regmap API to communicate with internal register.

Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com>
---
v5
 - Add registering failure handle for each clocks to prevent memory leak.
 - Update to use the latest clk_register. (Add clk_init_data).
 - Rename Kconfig menu's name and replace its position.

v4
 https://lkml.org/lkml/2012/6/27/183

 drivers/clk/Kconfig        |    6 +
 drivers/clk/Makefile       |    1 +
 drivers/clk/clk-max77686.c |  244 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 251 insertions(+), 0 deletions(-)
 create mode 100644 drivers/clk/clk-max77686.c

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 7f0b5ca..f300ec7 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -40,4 +40,10 @@ config COMMON_CLK_WM831X
           Supports the clocking subsystem of the WM831x/2x series of
 	  PMICs from Wolfson Microlectronics.
 
+config COMMON_CLK_MAX77686
+	tristate "Clock driver for Maxim 77686 MFD"
+	depends on MFD_MAX77686
+	---help---
+	  This driver supports Maxim 77686 crystal oscillator clock. 
+
 endmenu
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 5869ea3..97f5c57 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -13,3 +13,4 @@ obj-$(CONFIG_ARCH_INTEGRATOR)	+= versatile/
 
 # Chip specific
 obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
+obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
diff --git a/drivers/clk/clk-max77686.c b/drivers/clk/clk-max77686.c
new file mode 100644
index 0000000..ac5f543
--- /dev/null
+++ b/drivers/clk/clk-max77686.c
@@ -0,0 +1,244 @@
+/*
+ * clk-max77686.c - Clock driver for Maxim 77686
+ *
+ * Copyright (C) 2012 Samsung Electornics
+ * Jonghwa Lee <jonghwa3.lee@samsung.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/max77686.h>
+#include <linux/mfd/max77686-private.h>
+#include <linux/clk-provider.h>
+#include <linux/mutex.h>
+#include <linux/clkdev.h>
+
+enum {
+	MAX77686_CLK_AP = 0,
+	MAX77686_CLK_CP,
+	MAX77686_CLK_PMIC,
+	MAX77686_CLKS_NUM,
+};
+
+struct max77686_clk {
+	struct max77686_dev *iodev;
+	u32 mask;
+	struct clk_hw hw;
+	struct clk_lookup *lookup;
+};
+
+static struct max77686_clk *get_max77686_clk(struct clk_hw *hw)
+{
+	return container_of(hw, struct max77686_clk, hw);
+}
+
+static int max77686_clk_prepare(struct clk_hw *hw)
+{
+	struct max77686_clk *max77686;
+	int ret;
+
+	max77686 = get_max77686_clk(hw);
+	if (!max77686)
+		return -ENOMEM;
+
+	ret = regmap_update_bits(max77686->iodev->regmap,
+		MAX77686_REG_32KHZ, max77686->mask, max77686->mask);
+
+	return ret;
+}
+
+static void max77686_clk_unprepare(struct clk_hw *hw)
+{
+	struct max77686_clk *max77686;
+
+	max77686 = get_max77686_clk(hw);
+	if (!max77686)
+		return;
+
+	regmap_update_bits(max77686->iodev->regmap,
+		MAX77686_REG_32KHZ, max77686->mask, ~max77686->mask);
+}
+
+static int max77686_clk_is_enabled(struct clk_hw *hw)
+{
+	struct max77686_clk *max77686;
+	int ret;
+	u32 val;
+
+	max77686 = get_max77686_clk(hw);
+	if (!max77686)
+		return -ENOMEM;
+
+	ret = regmap_read(max77686->iodev->regmap,
+				MAX77686_REG_32KHZ, &val);
+
+	if (ret < 0)
+		return -EINVAL;
+
+	return val & max77686->mask;
+}
+
+static struct clk_ops max77686_clk_ops = {
+	.prepare	= max77686_clk_prepare,
+	.unprepare	= max77686_clk_unprepare,
+	.is_enabled	= max77686_clk_is_enabled,
+};
+
+static struct clk_init_data max77686_clks_init[MAX77686_CLKS_NUM] = {
+	[MAX77686_CLK_AP] = {
+		.name = "32khz_ap",
+		.ops = &max77686_clk_ops,
+		.flags = CLK_IS_ROOT,
+	},
+	[MAX77686_CLK_CP] = {
+		.name = "32khz_cp",
+		.ops = &max77686_clk_ops,
+		.flags = CLK_IS_ROOT,
+	},
+	[MAX77686_CLK_PMIC] = {
+		.name = "32khz_pmic",
+		.ops = &max77686_clk_ops,
+		.flags = CLK_IS_ROOT,
+	},
+};
+
+static int max77686_clk_register(struct device *dev,
+				struct max77686_clk *max77686)
+{
+	struct clk *clk;
+	struct clk_hw *hw = &max77686->hw;
+
+	clk = clk_register(dev, hw);
+
+	if (IS_ERR(clk))
+		return -ENOMEM;
+
+	max77686->lookup = devm_kzalloc(dev, sizeof(struct clk_lookup),
+					GFP_KERNEL);
+	if (IS_ERR(max77686->lookup))
+		return -ENOMEM;
+
+	max77686->lookup->con_id = hw->init->name;
+	max77686->lookup->clk = clk;
+
+	clkdev_add(max77686->lookup);
+
+	return 0;
+}
+
+static __devinit int max77686_clk_probe(struct platform_device *pdev)
+{
+	struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+	struct max77686_clk **max77686_clks;
+	int i, ret;
+
+	max77686_clks = devm_kzalloc(&pdev->dev, sizeof(struct max77686_clk *)
+					* MAX77686_CLKS_NUM, GFP_KERNEL);
+	if (IS_ERR(max77686_clks))
+		return -ENOMEM;
+
+	for (i = 0; i < MAX77686_CLKS_NUM; i++) {
+		max77686_clks[i] = devm_kzalloc(&pdev->dev,
+					sizeof(struct max77686_clk), GFP_KERNEL);
+		if (IS_ERR(max77686_clks[i]))
+			return -ENOMEM;
+	}
+
+	for (i = 0; i < MAX77686_CLKS_NUM; i++) {
+		max77686_clks[i]->iodev = iodev;
+		max77686_clks[i]->mask = 1 << i;
+		max77686_clks[i]->hw.init = &max77686_clks_init[i];
+
+		ret = max77686_clk_register(&pdev->dev, max77686_clks[i]);
+		if (ret) {
+			switch (i) {
+			case MAX77686_CLK_AP:
+				dev_err(&pdev->dev, "Fail to register CLK_AP\n");
+				goto err_clk_ap;
+				break;
+			case MAX77686_CLK_CP:
+				dev_err(&pdev->dev, "Fail to register CLK_CP\n");
+				goto err_clk_cp;
+				break;
+			case MAX77686_CLK_PMIC:
+				dev_err(&pdev->dev, "Fail to register CLK_PMIC\n");
+				goto err_clk_pmic;
+			}
+		}
+	}
+
+	platform_set_drvdata(pdev, max77686_clks);
+
+	goto out;
+
+err_clk_pmic:
+	clkdev_drop(max77686_clks[MAX77686_CLK_CP]->lookup);
+	kfree(max77686_clks[MAX77686_CLK_CP]->hw.clk);
+err_clk_cp:
+	clkdev_drop(max77686_clks[MAX77686_CLK_AP]->lookup);
+	kfree(max77686_clks[MAX77686_CLK_AP]->hw.clk);
+err_clk_ap:
+out:
+	return ret;
+}
+
+static int __devexit max77686_clk_remove(struct platform_device *pdev)
+{
+	struct max77686_clk **max77686_clks = platform_get_drvdata(pdev);
+	int i;
+
+	for (i = 0; i < MAX77686_CLKS_NUM; i++) {
+		clkdev_drop(max77686_clks[i]->lookup);
+		kfree(max77686_clks[i]->hw.clk);
+	}
+	return 0;
+}
+
+static const struct platform_device_id max77686_clk_id[] = {
+	{ "max77686-clk", 0},
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, max77686_clk_id);
+
+static struct platform_driver max77686_clk_driver = {
+	.driver = {
+		.name  = "max77686-clk",
+		.owner = THIS_MODULE,
+	},
+	.probe = max77686_clk_probe,
+	.remove = __devexit_p(max77686_clk_remove),
+	.id_table = max77686_clk_id,
+};
+
+static int __init max77686_clk_init(void)
+{
+	return platform_driver_register(&max77686_clk_driver);
+}
+subsys_initcall(max77686_clk_init);
+
+static void __init max77686_clk_cleanup(void)
+{
+	platform_driver_unregister(&max77686_clk_driver);
+}
+module_exit(max77686_clk_cleanup);
+
+MODULE_DESCRIPTION("MAXIM 77686 Clock Driver");
+MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>");
+MODULE_LICENSE("GPL");
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH] clock: max77686: Add driver for Maxim 77686 32Khz crystal oscillator.
  2012-08-28  8:54 [PATCH] clock: max77686: Add driver for Maxim 77686 32Khz crystal oscillator Jonghwa Lee
@ 2012-09-07  0:04 ` Mike Turquette
  2012-09-10  1:54   ` jonghwa3.lee
  0 siblings, 1 reply; 8+ messages in thread
From: Mike Turquette @ 2012-09-07  0:04 UTC (permalink / raw)
  To: Jonghwa Lee, linux-kernel; +Cc: Arnd Bergmann, H Hartley Sweeten, Jonghwa Lee

Quoting Jonghwa Lee (2012-08-28 01:54:28)
> This patch supports max77686 mfd's clock driver using common clock frame work.
> max77686 has 3 clock ouputs which all are generated from crystal oscillator and
> SOC can enable/disable them via I2C bus. All clocks are fixed-rate clock sources
> so that it doesn't supply interface for changing clock rate.
> Driver uses regmap API to communicate with internal register.
> 
> Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com>

Looks good.  I've taken this into clk-next.

Regards,
Mike

> ---
> v5
>  - Add registering failure handle for each clocks to prevent memory leak.
>  - Update to use the latest clk_register. (Add clk_init_data).
>  - Rename Kconfig menu's name and replace its position.
> 
> v4
>  https://lkml.org/lkml/2012/6/27/183
> 
>  drivers/clk/Kconfig        |    6 +
>  drivers/clk/Makefile       |    1 +
>  drivers/clk/clk-max77686.c |  244 ++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 251 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/clk/clk-max77686.c
> 
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index 7f0b5ca..f300ec7 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -40,4 +40,10 @@ config COMMON_CLK_WM831X
>            Supports the clocking subsystem of the WM831x/2x series of
>           PMICs from Wolfson Microlectronics.
>  
> +config COMMON_CLK_MAX77686
> +       tristate "Clock driver for Maxim 77686 MFD"
> +       depends on MFD_MAX77686
> +       ---help---
> +         This driver supports Maxim 77686 crystal oscillator clock. 
> +
>  endmenu
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index 5869ea3..97f5c57 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -13,3 +13,4 @@ obj-$(CONFIG_ARCH_INTEGRATOR) += versatile/
>  
>  # Chip specific
>  obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
> +obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
> diff --git a/drivers/clk/clk-max77686.c b/drivers/clk/clk-max77686.c
> new file mode 100644
> index 0000000..ac5f543
> --- /dev/null
> +++ b/drivers/clk/clk-max77686.c
> @@ -0,0 +1,244 @@
> +/*
> + * clk-max77686.c - Clock driver for Maxim 77686
> + *
> + * Copyright (C) 2012 Samsung Electornics
> + * Jonghwa Lee <jonghwa3.lee@samsung.com>
> + *
> + * This program is free software; you can redistribute  it and/or modify it
> + * under  the terms of  the GNU General  Public License as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + *
> + * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/err.h>
> +#include <linux/platform_device.h>
> +#include <linux/mfd/max77686.h>
> +#include <linux/mfd/max77686-private.h>
> +#include <linux/clk-provider.h>
> +#include <linux/mutex.h>
> +#include <linux/clkdev.h>
> +
> +enum {
> +       MAX77686_CLK_AP = 0,
> +       MAX77686_CLK_CP,
> +       MAX77686_CLK_PMIC,
> +       MAX77686_CLKS_NUM,
> +};
> +
> +struct max77686_clk {
> +       struct max77686_dev *iodev;
> +       u32 mask;
> +       struct clk_hw hw;
> +       struct clk_lookup *lookup;
> +};
> +
> +static struct max77686_clk *get_max77686_clk(struct clk_hw *hw)
> +{
> +       return container_of(hw, struct max77686_clk, hw);
> +}
> +
> +static int max77686_clk_prepare(struct clk_hw *hw)
> +{
> +       struct max77686_clk *max77686;
> +       int ret;
> +
> +       max77686 = get_max77686_clk(hw);
> +       if (!max77686)
> +               return -ENOMEM;
> +
> +       ret = regmap_update_bits(max77686->iodev->regmap,
> +               MAX77686_REG_32KHZ, max77686->mask, max77686->mask);
> +
> +       return ret;
> +}
> +
> +static void max77686_clk_unprepare(struct clk_hw *hw)
> +{
> +       struct max77686_clk *max77686;
> +
> +       max77686 = get_max77686_clk(hw);
> +       if (!max77686)
> +               return;
> +
> +       regmap_update_bits(max77686->iodev->regmap,
> +               MAX77686_REG_32KHZ, max77686->mask, ~max77686->mask);
> +}
> +
> +static int max77686_clk_is_enabled(struct clk_hw *hw)
> +{
> +       struct max77686_clk *max77686;
> +       int ret;
> +       u32 val;
> +
> +       max77686 = get_max77686_clk(hw);
> +       if (!max77686)
> +               return -ENOMEM;
> +
> +       ret = regmap_read(max77686->iodev->regmap,
> +                               MAX77686_REG_32KHZ, &val);
> +
> +       if (ret < 0)
> +               return -EINVAL;
> +
> +       return val & max77686->mask;
> +}
> +
> +static struct clk_ops max77686_clk_ops = {
> +       .prepare        = max77686_clk_prepare,
> +       .unprepare      = max77686_clk_unprepare,
> +       .is_enabled     = max77686_clk_is_enabled,
> +};
> +
> +static struct clk_init_data max77686_clks_init[MAX77686_CLKS_NUM] = {
> +       [MAX77686_CLK_AP] = {
> +               .name = "32khz_ap",
> +               .ops = &max77686_clk_ops,
> +               .flags = CLK_IS_ROOT,
> +       },
> +       [MAX77686_CLK_CP] = {
> +               .name = "32khz_cp",
> +               .ops = &max77686_clk_ops,
> +               .flags = CLK_IS_ROOT,
> +       },
> +       [MAX77686_CLK_PMIC] = {
> +               .name = "32khz_pmic",
> +               .ops = &max77686_clk_ops,
> +               .flags = CLK_IS_ROOT,
> +       },
> +};
> +
> +static int max77686_clk_register(struct device *dev,
> +                               struct max77686_clk *max77686)
> +{
> +       struct clk *clk;
> +       struct clk_hw *hw = &max77686->hw;
> +
> +       clk = clk_register(dev, hw);
> +
> +       if (IS_ERR(clk))
> +               return -ENOMEM;
> +
> +       max77686->lookup = devm_kzalloc(dev, sizeof(struct clk_lookup),
> +                                       GFP_KERNEL);
> +       if (IS_ERR(max77686->lookup))
> +               return -ENOMEM;
> +
> +       max77686->lookup->con_id = hw->init->name;
> +       max77686->lookup->clk = clk;
> +
> +       clkdev_add(max77686->lookup);
> +
> +       return 0;
> +}
> +
> +static __devinit int max77686_clk_probe(struct platform_device *pdev)
> +{
> +       struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
> +       struct max77686_clk **max77686_clks;
> +       int i, ret;
> +
> +       max77686_clks = devm_kzalloc(&pdev->dev, sizeof(struct max77686_clk *)
> +                                       * MAX77686_CLKS_NUM, GFP_KERNEL);
> +       if (IS_ERR(max77686_clks))
> +               return -ENOMEM;
> +
> +       for (i = 0; i < MAX77686_CLKS_NUM; i++) {
> +               max77686_clks[i] = devm_kzalloc(&pdev->dev,
> +                                       sizeof(struct max77686_clk), GFP_KERNEL);
> +               if (IS_ERR(max77686_clks[i]))
> +                       return -ENOMEM;
> +       }
> +
> +       for (i = 0; i < MAX77686_CLKS_NUM; i++) {
> +               max77686_clks[i]->iodev = iodev;
> +               max77686_clks[i]->mask = 1 << i;
> +               max77686_clks[i]->hw.init = &max77686_clks_init[i];
> +
> +               ret = max77686_clk_register(&pdev->dev, max77686_clks[i]);
> +               if (ret) {
> +                       switch (i) {
> +                       case MAX77686_CLK_AP:
> +                               dev_err(&pdev->dev, "Fail to register CLK_AP\n");
> +                               goto err_clk_ap;
> +                               break;
> +                       case MAX77686_CLK_CP:
> +                               dev_err(&pdev->dev, "Fail to register CLK_CP\n");
> +                               goto err_clk_cp;
> +                               break;
> +                       case MAX77686_CLK_PMIC:
> +                               dev_err(&pdev->dev, "Fail to register CLK_PMIC\n");
> +                               goto err_clk_pmic;
> +                       }
> +               }
> +       }
> +
> +       platform_set_drvdata(pdev, max77686_clks);
> +
> +       goto out;
> +
> +err_clk_pmic:
> +       clkdev_drop(max77686_clks[MAX77686_CLK_CP]->lookup);
> +       kfree(max77686_clks[MAX77686_CLK_CP]->hw.clk);
> +err_clk_cp:
> +       clkdev_drop(max77686_clks[MAX77686_CLK_AP]->lookup);
> +       kfree(max77686_clks[MAX77686_CLK_AP]->hw.clk);
> +err_clk_ap:
> +out:
> +       return ret;
> +}
> +
> +static int __devexit max77686_clk_remove(struct platform_device *pdev)
> +{
> +       struct max77686_clk **max77686_clks = platform_get_drvdata(pdev);
> +       int i;
> +
> +       for (i = 0; i < MAX77686_CLKS_NUM; i++) {
> +               clkdev_drop(max77686_clks[i]->lookup);
> +               kfree(max77686_clks[i]->hw.clk);
> +       }
> +       return 0;
> +}
> +
> +static const struct platform_device_id max77686_clk_id[] = {
> +       { "max77686-clk", 0},
> +       { },
> +};
> +MODULE_DEVICE_TABLE(platform, max77686_clk_id);
> +
> +static struct platform_driver max77686_clk_driver = {
> +       .driver = {
> +               .name  = "max77686-clk",
> +               .owner = THIS_MODULE,
> +       },
> +       .probe = max77686_clk_probe,
> +       .remove = __devexit_p(max77686_clk_remove),
> +       .id_table = max77686_clk_id,
> +};
> +
> +static int __init max77686_clk_init(void)
> +{
> +       return platform_driver_register(&max77686_clk_driver);
> +}
> +subsys_initcall(max77686_clk_init);
> +
> +static void __init max77686_clk_cleanup(void)
> +{
> +       platform_driver_unregister(&max77686_clk_driver);
> +}
> +module_exit(max77686_clk_cleanup);
> +
> +MODULE_DESCRIPTION("MAXIM 77686 Clock Driver");
> +MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>");
> +MODULE_LICENSE("GPL");
> -- 
> 1.7.4.1

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] clock: max77686: Add driver for Maxim 77686 32Khz crystal oscillator.
  2012-09-07  0:04 ` Mike Turquette
@ 2012-09-10  1:54   ` jonghwa3.lee
  0 siblings, 0 replies; 8+ messages in thread
From: jonghwa3.lee @ 2012-09-10  1:54 UTC (permalink / raw)
  To: Mike Turquette; +Cc: linux-kernel, Arnd Bergmann, H Hartley Sweeten

On 2012년 09월 07일 09:04, Mike Turquette wrote:

> Quoting Jonghwa Lee (2012-08-28 01:54:28)
>> This patch supports max77686 mfd's clock driver using common clock frame work.
>> max77686 has 3 clock ouputs which all are generated from crystal oscillator and
>> SOC can enable/disable them via I2C bus. All clocks are fixed-rate clock sources
>> so that it doesn't supply interface for changing clock rate.
>> Driver uses regmap API to communicate with internal register.
>>
>> Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com>
> 
> Looks good.  I've taken this into clk-next.
> 
> Regards,
> Mike
> 


Thanks,

Best Regards,
Jonghwa

>> ---
>> v5
>>  - Add registering failure handle for each clocks to prevent memory leak.
>>  - Update to use the latest clk_register. (Add clk_init_data).
>>  - Rename Kconfig menu's name and replace its position.
>>
>> v4
>>  https://lkml.org/lkml/2012/6/27/183
>>
>>  drivers/clk/Kconfig        |    6 +
>>  drivers/clk/Makefile       |    1 +
>>  drivers/clk/clk-max77686.c |  244 ++++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 251 insertions(+), 0 deletions(-)
>>  create mode 100644 drivers/clk/clk-max77686.c
>>
>> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
>> index 7f0b5ca..f300ec7 100644
>> --- a/drivers/clk/Kconfig
>> +++ b/drivers/clk/Kconfig
>> @@ -40,4 +40,10 @@ config COMMON_CLK_WM831X
>>            Supports the clocking subsystem of the WM831x/2x series of
>>           PMICs from Wolfson Microlectronics.
>>  
>> +config COMMON_CLK_MAX77686
>> +       tristate "Clock driver for Maxim 77686 MFD"
>> +       depends on MFD_MAX77686
>> +       ---help---
>> +         This driver supports Maxim 77686 crystal oscillator clock. 
>> +
>>  endmenu
>> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
>> index 5869ea3..97f5c57 100644
>> --- a/drivers/clk/Makefile
>> +++ b/drivers/clk/Makefile
>> @@ -13,3 +13,4 @@ obj-$(CONFIG_ARCH_INTEGRATOR) += versatile/
>>  
>>  # Chip specific
>>  obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
>> +obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
>> diff --git a/drivers/clk/clk-max77686.c b/drivers/clk/clk-max77686.c
>> new file mode 100644
>> index 0000000..ac5f543
>> --- /dev/null
>> +++ b/drivers/clk/clk-max77686.c
>> @@ -0,0 +1,244 @@
>> +/*
>> + * clk-max77686.c - Clock driver for Maxim 77686
>> + *
>> + * Copyright (C) 2012 Samsung Electornics
>> + * Jonghwa Lee <jonghwa3.lee@samsung.com>
>> + *
>> + * This program is free software; you can redistribute  it and/or modify it
>> + * under  the terms of  the GNU General  Public License as published by the
>> + * Free Software Foundation;  either version 2 of the  License, or (at your
>> + * option) any later version.
>> + *
>> + * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
>> + *
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/slab.h>
>> +#include <linux/err.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/mfd/max77686.h>
>> +#include <linux/mfd/max77686-private.h>
>> +#include <linux/clk-provider.h>
>> +#include <linux/mutex.h>
>> +#include <linux/clkdev.h>
>> +
>> +enum {
>> +       MAX77686_CLK_AP = 0,
>> +       MAX77686_CLK_CP,
>> +       MAX77686_CLK_PMIC,
>> +       MAX77686_CLKS_NUM,
>> +};
>> +
>> +struct max77686_clk {
>> +       struct max77686_dev *iodev;
>> +       u32 mask;
>> +       struct clk_hw hw;
>> +       struct clk_lookup *lookup;
>> +};
>> +
>> +static struct max77686_clk *get_max77686_clk(struct clk_hw *hw)
>> +{
>> +       return container_of(hw, struct max77686_clk, hw);
>> +}
>> +
>> +static int max77686_clk_prepare(struct clk_hw *hw)
>> +{
>> +       struct max77686_clk *max77686;
>> +       int ret;
>> +
>> +       max77686 = get_max77686_clk(hw);
>> +       if (!max77686)
>> +               return -ENOMEM;
>> +
>> +       ret = regmap_update_bits(max77686->iodev->regmap,
>> +               MAX77686_REG_32KHZ, max77686->mask, max77686->mask);
>> +
>> +       return ret;
>> +}
>> +
>> +static void max77686_clk_unprepare(struct clk_hw *hw)
>> +{
>> +       struct max77686_clk *max77686;
>> +
>> +       max77686 = get_max77686_clk(hw);
>> +       if (!max77686)
>> +               return;
>> +
>> +       regmap_update_bits(max77686->iodev->regmap,
>> +               MAX77686_REG_32KHZ, max77686->mask, ~max77686->mask);
>> +}
>> +
>> +static int max77686_clk_is_enabled(struct clk_hw *hw)
>> +{
>> +       struct max77686_clk *max77686;
>> +       int ret;
>> +       u32 val;
>> +
>> +       max77686 = get_max77686_clk(hw);
>> +       if (!max77686)
>> +               return -ENOMEM;
>> +
>> +       ret = regmap_read(max77686->iodev->regmap,
>> +                               MAX77686_REG_32KHZ, &val);
>> +
>> +       if (ret < 0)
>> +               return -EINVAL;
>> +
>> +       return val & max77686->mask;
>> +}
>> +
>> +static struct clk_ops max77686_clk_ops = {
>> +       .prepare        = max77686_clk_prepare,
>> +       .unprepare      = max77686_clk_unprepare,
>> +       .is_enabled     = max77686_clk_is_enabled,
>> +};
>> +
>> +static struct clk_init_data max77686_clks_init[MAX77686_CLKS_NUM] = {
>> +       [MAX77686_CLK_AP] = {
>> +               .name = "32khz_ap",
>> +               .ops = &max77686_clk_ops,
>> +               .flags = CLK_IS_ROOT,
>> +       },
>> +       [MAX77686_CLK_CP] = {
>> +               .name = "32khz_cp",
>> +               .ops = &max77686_clk_ops,
>> +               .flags = CLK_IS_ROOT,
>> +       },
>> +       [MAX77686_CLK_PMIC] = {
>> +               .name = "32khz_pmic",
>> +               .ops = &max77686_clk_ops,
>> +               .flags = CLK_IS_ROOT,
>> +       },
>> +};
>> +
>> +static int max77686_clk_register(struct device *dev,
>> +                               struct max77686_clk *max77686)
>> +{
>> +       struct clk *clk;
>> +       struct clk_hw *hw = &max77686->hw;
>> +
>> +       clk = clk_register(dev, hw);
>> +
>> +       if (IS_ERR(clk))
>> +               return -ENOMEM;
>> +
>> +       max77686->lookup = devm_kzalloc(dev, sizeof(struct clk_lookup),
>> +                                       GFP_KERNEL);
>> +       if (IS_ERR(max77686->lookup))
>> +               return -ENOMEM;
>> +
>> +       max77686->lookup->con_id = hw->init->name;
>> +       max77686->lookup->clk = clk;
>> +
>> +       clkdev_add(max77686->lookup);
>> +
>> +       return 0;
>> +}
>> +
>> +static __devinit int max77686_clk_probe(struct platform_device *pdev)
>> +{
>> +       struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
>> +       struct max77686_clk **max77686_clks;
>> +       int i, ret;
>> +
>> +       max77686_clks = devm_kzalloc(&pdev->dev, sizeof(struct max77686_clk *)
>> +                                       * MAX77686_CLKS_NUM, GFP_KERNEL);
>> +       if (IS_ERR(max77686_clks))
>> +               return -ENOMEM;
>> +
>> +       for (i = 0; i < MAX77686_CLKS_NUM; i++) {
>> +               max77686_clks[i] = devm_kzalloc(&pdev->dev,
>> +                                       sizeof(struct max77686_clk), GFP_KERNEL);
>> +               if (IS_ERR(max77686_clks[i]))
>> +                       return -ENOMEM;
>> +       }
>> +
>> +       for (i = 0; i < MAX77686_CLKS_NUM; i++) {
>> +               max77686_clks[i]->iodev = iodev;
>> +               max77686_clks[i]->mask = 1 << i;
>> +               max77686_clks[i]->hw.init = &max77686_clks_init[i];
>> +
>> +               ret = max77686_clk_register(&pdev->dev, max77686_clks[i]);
>> +               if (ret) {
>> +                       switch (i) {
>> +                       case MAX77686_CLK_AP:
>> +                               dev_err(&pdev->dev, "Fail to register CLK_AP\n");
>> +                               goto err_clk_ap;
>> +                               break;
>> +                       case MAX77686_CLK_CP:
>> +                               dev_err(&pdev->dev, "Fail to register CLK_CP\n");
>> +                               goto err_clk_cp;
>> +                               break;
>> +                       case MAX77686_CLK_PMIC:
>> +                               dev_err(&pdev->dev, "Fail to register CLK_PMIC\n");
>> +                               goto err_clk_pmic;
>> +                       }
>> +               }
>> +       }
>> +
>> +       platform_set_drvdata(pdev, max77686_clks);
>> +
>> +       goto out;
>> +
>> +err_clk_pmic:
>> +       clkdev_drop(max77686_clks[MAX77686_CLK_CP]->lookup);
>> +       kfree(max77686_clks[MAX77686_CLK_CP]->hw.clk);
>> +err_clk_cp:
>> +       clkdev_drop(max77686_clks[MAX77686_CLK_AP]->lookup);
>> +       kfree(max77686_clks[MAX77686_CLK_AP]->hw.clk);
>> +err_clk_ap:
>> +out:
>> +       return ret;
>> +}
>> +
>> +static int __devexit max77686_clk_remove(struct platform_device *pdev)
>> +{
>> +       struct max77686_clk **max77686_clks = platform_get_drvdata(pdev);
>> +       int i;
>> +
>> +       for (i = 0; i < MAX77686_CLKS_NUM; i++) {
>> +               clkdev_drop(max77686_clks[i]->lookup);
>> +               kfree(max77686_clks[i]->hw.clk);
>> +       }
>> +       return 0;
>> +}
>> +
>> +static const struct platform_device_id max77686_clk_id[] = {
>> +       { "max77686-clk", 0},
>> +       { },
>> +};
>> +MODULE_DEVICE_TABLE(platform, max77686_clk_id);
>> +
>> +static struct platform_driver max77686_clk_driver = {
>> +       .driver = {
>> +               .name  = "max77686-clk",
>> +               .owner = THIS_MODULE,
>> +       },
>> +       .probe = max77686_clk_probe,
>> +       .remove = __devexit_p(max77686_clk_remove),
>> +       .id_table = max77686_clk_id,
>> +};
>> +
>> +static int __init max77686_clk_init(void)
>> +{
>> +       return platform_driver_register(&max77686_clk_driver);
>> +}
>> +subsys_initcall(max77686_clk_init);
>> +
>> +static void __init max77686_clk_cleanup(void)
>> +{
>> +       platform_driver_unregister(&max77686_clk_driver);
>> +}
>> +module_exit(max77686_clk_cleanup);
>> +
>> +MODULE_DESCRIPTION("MAXIM 77686 Clock Driver");
>> +MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>");
>> +MODULE_LICENSE("GPL");
>> -- 
>> 1.7.4.1
> 



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] clock: max77686: Add driver for Maxim 77686 32KHz crystal oscillator
  2012-06-01 16:13 ` Arnd Bergmann
  2012-06-01 16:21   ` Mark Brown
@ 2012-06-11  5:05   ` jonghwa3.lee
  1 sibling, 0 replies; 8+ messages in thread
From: jonghwa3.lee @ 2012-06-11  5:05 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-kernel, Mike Turquette, H Hartley Sweeten, Mark Brown,
	MyungJoo Ham, Kyungmin Park

Hi, Arnd.
Sorry for that reply is late. I had some personal reasons so i couldn't
check e-mail all last week. I just read your comments today.

On 2012년 06월 02일 01:13, Arnd Bergmann wrote:

> On Friday 01 June 2012, Jonghwa Lee wrote:
>> +
>> +#ifdef COMMON_CONFIG_CLK
> 
> Two comments on this one:
> 
> 1. It should be CONFIG_COMMON_CLK, not COMMON_CONFIG_CLK, I suppose. The symbol
> you are testing for is never defined so your code does not even get built.
> I suppose you did not test the version you are sending ...
> 
> 2. There is no use in enclosing an entire file in #ifdef. Instead, make the Kconfig
> symbol depend on COMMON_CLK.
> 


I did build test only without #ifdef statement before upload. And i
wrote this line right before send the code. I made mistake at that time.
Anyway, I'll change this condition as you commented.

>> +#define to_max77686_clk(__name)		container_of(hw,		\
>> +					struct max77686_clk, __name)
> 
> This use of container_of() is very unusual and confusing, because the argument
> into your macro is the member of the struct, not the variable that you are basing
> from. You should not need the macro at all, so please try to remove it.
> 


Yes, i agree that this macro makes some confuse.

>> +struct max77686_clk {
>> +	struct max77686_dev *iodev;
>> +	struct clk_hw clk32khz_ap_hw;
>> +	struct clk_hw clk32khz_cp_hw;
>> +	struct clk_hw clk32khz_pmic_hw;
>> +};
>> +
>> +static struct clk *clk32khz_ap;
>> +static struct clk *clk32khz_cp;
>> +static struct clk *clk32khz_pmic;
>> +static char *max77686_clk[] = {
>> +	"32khap",
>> +	"32khcp",
>> +	"p32kh",
>> +};
> 
> With these static definitions, you can only have a single max77686 device in the
> system. Better remove these symbols.


Okay, I'll apply it.

> 
>> +static struct max77686_clk *get_max77686_clk(struct clk_hw *hw)
>> +{
>> +	struct clk *clk = hw->clk;
>> +	if (clk == clk32khz_ap)
>> +		return to_max77686_clk(clk32khz_ap_hw);
>> +	else if (clk == clk32khz_cp)
>> +		return to_max77686_clk(clk32khz_cp_hw);
>> +	else if (clk == clk32khz_pmic)
>> +		return to_max77686_clk(clk32khz_pmic_hw);
>> +	else
>> +		return NULL;
>> +}
> 
> I can only assume that you meant this to be
> 
> struct max77686_clk {
> 	struct max77686_dev *iodev;
> 	u32 mask;
> 	struct clk_hw hw;
> };
> static struct max77686_clk *get_max77686_clk(struct clk_hw *hw)
> {
> 	return container_of(hw, struct max77686_clk, hw)->iodev;
> }
> 
> You probably misunderstood the person who was suggesting you use
> container_of(). Note that this function is so simple that you
> probably don't even need it: just open-code the container_of.
>


I think to_max77686_clk macro makes you uncomfortable, I'll modify them
with considering your comments. And your get_max77668_clk() return wrong
type pointer since it has to return struct max77696_clk pointer.

 

Thanks.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] clock: max77686: Add driver for Maxim 77686 32KHz crystal oscillator
  2012-06-01 16:21   ` Mark Brown
@ 2012-06-02  2:50     ` Arnd Bergmann
  0 siblings, 0 replies; 8+ messages in thread
From: Arnd Bergmann @ 2012-06-02  2:50 UTC (permalink / raw)
  To: Mark Brown
  Cc: Jonghwa Lee, linux-kernel, Mike Turquette, H Hartley Sweeten,
	MyungJoo Ham, Kyungmin Park

On Friday 01 June 2012, Mark Brown wrote:
> On Fri, Jun 01, 2012 at 04:13:33PM +0000, Arnd Bergmann wrote:
> 
> > You should also have an of_device_id table so you can match this driver from
> > device tree definitions.
> 
> There has been...  some discussion on the value of doing this for MFD
> cells where the MFD cell has no reuse capability (eg, when the driver
> has very little abstraction from the top level chip binding) since we
> don't really get anything from the abstraction if the driver can't be
> reused and may limit ourselves (and others) in future.

Ok, makes sense.

	Arnd

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] clock: max77686: Add driver for Maxim 77686 32KHz crystal oscillator
  2012-06-01 16:13 ` Arnd Bergmann
@ 2012-06-01 16:21   ` Mark Brown
  2012-06-02  2:50     ` Arnd Bergmann
  2012-06-11  5:05   ` jonghwa3.lee
  1 sibling, 1 reply; 8+ messages in thread
From: Mark Brown @ 2012-06-01 16:21 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Jonghwa Lee, linux-kernel, Mike Turquette, H Hartley Sweeten,
	MyungJoo Ham, Kyungmin Park

[-- Attachment #1: Type: text/plain, Size: 506 bytes --]

On Fri, Jun 01, 2012 at 04:13:33PM +0000, Arnd Bergmann wrote:

> You should also have an of_device_id table so you can match this driver from
> device tree definitions.

There has been...  some discussion on the value of doing this for MFD
cells where the MFD cell has no reuse capability (eg, when the driver
has very little abstraction from the top level chip binding) since we
don't really get anything from the abstraction if the driver can't be
reused and may limit ourselves (and others) in future.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH] clock: max77686: Add driver for Maxim 77686 32KHz crystal oscillator
  2012-06-01  9:08 [PATCH] clock: max77686: Add driver for Maxim 77686 32KHz " Jonghwa Lee
@ 2012-06-01 16:13 ` Arnd Bergmann
  2012-06-01 16:21   ` Mark Brown
  2012-06-11  5:05   ` jonghwa3.lee
  0 siblings, 2 replies; 8+ messages in thread
From: Arnd Bergmann @ 2012-06-01 16:13 UTC (permalink / raw)
  To: Jonghwa Lee
  Cc: linux-kernel, Mike Turquette, H Hartley Sweeten, Mark Brown,
	MyungJoo Ham, Kyungmin Park

On Friday 01 June 2012, Jonghwa Lee wrote:
> +
> +#ifdef COMMON_CONFIG_CLK

Two comments on this one:

1. It should be CONFIG_COMMON_CLK, not COMMON_CONFIG_CLK, I suppose. The symbol
you are testing for is never defined so your code does not even get built.
I suppose you did not test the version you are sending ...

2. There is no use in enclosing an entire file in #ifdef. Instead, make the Kconfig
symbol depend on COMMON_CLK.

> +#define to_max77686_clk(__name)		container_of(hw,		\
> +					struct max77686_clk, __name)

This use of container_of() is very unusual and confusing, because the argument
into your macro is the member of the struct, not the variable that you are basing
from. You should not need the macro at all, so please try to remove it.

> +struct max77686_clk {
> +	struct max77686_dev *iodev;
> +	struct clk_hw clk32khz_ap_hw;
> +	struct clk_hw clk32khz_cp_hw;
> +	struct clk_hw clk32khz_pmic_hw;
> +};
> +
> +static struct clk *clk32khz_ap;
> +static struct clk *clk32khz_cp;
> +static struct clk *clk32khz_pmic;
> +static char *max77686_clk[] = {
> +	"32khap",
> +	"32khcp",
> +	"p32kh",
> +};

With these static definitions, you can only have a single max77686 device in the
system. Better remove these symbols.

> +static struct max77686_clk *get_max77686_clk(struct clk_hw *hw)
> +{
> +	struct clk *clk = hw->clk;
> +	if (clk == clk32khz_ap)
> +		return to_max77686_clk(clk32khz_ap_hw);
> +	else if (clk == clk32khz_cp)
> +		return to_max77686_clk(clk32khz_cp_hw);
> +	else if (clk == clk32khz_pmic)
> +		return to_max77686_clk(clk32khz_pmic_hw);
> +	else
> +		return NULL;
> +}

I can only assume that you meant this to be

struct max77686_clk {
	struct max77686_dev *iodev;
	u32 mask;
	struct clk_hw hw;
};
static struct max77686_clk *get_max77686_clk(struct clk_hw *hw)
{
	return container_of(hw, struct max77686_clk, hw)->iodev;
}

You probably misunderstood the person who was suggesting you use
container_of(). Note that this function is so simple that you
probably don't even need it: just open-code the container_of.

> +static const struct platform_device_id max77686_clk_id[] = {
> +	{ "max77686-clk", 0},
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(platform, max77686_clk_id);
> +
> +static struct platform_driver max77686_clk_driver = {
> +	.driver = {
> +		.name  = "max77686-clk",
> +		.owner = THIS_MODULE,
> +	},
> +	.probe = max77686_clk_probe,
> +	.remove = __devexit_p(max77686_clk_remove),
> +	.id_table = max77686_clk_id,
> +};

You should also have an of_device_id table so you can match this driver from
device tree definitions.

	Arnd

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH] clock: max77686: Add driver for Maxim 77686 32KHz crystal oscillator
@ 2012-06-01  9:08 Jonghwa Lee
  2012-06-01 16:13 ` Arnd Bergmann
  0 siblings, 1 reply; 8+ messages in thread
From: Jonghwa Lee @ 2012-06-01  9:08 UTC (permalink / raw)
  To: linux-kernel, Mike Turquette, Arnd Bergmann
  Cc: H Hartley Sweeten, Mark Brown, Jonghwa Lee, MyungJoo Ham, Kyungmin Park

Maxim 77686 has three 32KHz clock outputs through the its crystal oscillator.
This driver can control those ouputs by I2C bus. The clocks are used to supply
to SOC and peripheral chips as a clock source and can be enabled/disabled only.
It uses regmap interface to communicate with I2C bus.

Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com>
Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/clk/Kconfig        |    8 ++
 drivers/clk/Makefile       |    2 +
 drivers/clk/clk-max77686.c |  214 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 224 insertions(+), 0 deletions(-)
 create mode 100644 drivers/clk/clk-max77686.c

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 165e1fe..dda0e2e 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -45,3 +45,11 @@ config COMMON_CLK_DEBUG
 	  clk_notifier_count.
 
 endmenu
+
+config CLK_MAX77686
+	tristate "Maxim 77686 32KHz crystal oscillator driver"
+	depends on MFD_MAX77686
+	---help---
+	  This driver supports for Maxim 77686's crystal oscillator.
+	  Maxim 77686 has three 32KHz buffered outputs and It can
+	  controls them over I2C bus.
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 1f736bc..a952555b 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -2,3 +2,5 @@
 obj-$(CONFIG_CLKDEV_LOOKUP)	+= clkdev.o
 obj-$(CONFIG_COMMON_CLK)	+= clk.o clk-fixed-rate.o clk-gate.o \
 				   clk-mux.o clk-divider.o
+
+obj-$(CONFIG_CLK_MAX77686)	+= clk-max77686.o
diff --git a/drivers/clk/clk-max77686.c b/drivers/clk/clk-max77686.c
new file mode 100644
index 0000000..8c27cd6
--- /dev/null
+++ b/drivers/clk/clk-max77686.c
@@ -0,0 +1,214 @@
+/*
+ * clk-max77686.c - Clock driver for Maxim 77686
+ * 
+ * Copyright (C) 2012 Samsung Electornics
+ * Jonghwa Lee <jonghwa3.lee@samsung.com> 
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifdef COMMON_CONFIG_CLK
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/max77686.h>
+#include <linux/mfd/max77686-private.h>
+#include <linux/clk-private.h>
+
+#define to_max77686_clk(__name)		container_of(hw,		\
+					struct max77686_clk, __name)
+struct max77686_clk {
+	struct max77686_dev *iodev;
+	struct clk_hw clk32khz_ap_hw;
+	struct clk_hw clk32khz_cp_hw;
+	struct clk_hw clk32khz_pmic_hw;
+};
+
+static struct clk *clk32khz_ap;
+static struct clk *clk32khz_cp;
+static struct clk *clk32khz_pmic;
+static char *max77686_clk[] = {
+	"32khap",
+	"32khcp",
+	"p32kh",
+};
+
+static struct max77686_clk *get_max77686_clk(struct clk_hw *hw)
+{
+	struct clk *clk = hw->clk;
+	if (clk == clk32khz_ap)
+		return to_max77686_clk(clk32khz_ap_hw);
+	else if (clk == clk32khz_cp)
+		return to_max77686_clk(clk32khz_cp_hw);
+	else if (clk == clk32khz_pmic)
+		return to_max77686_clk(clk32khz_pmic_hw);
+	else
+		return NULL;
+}
+
+static int max77686_clk_get_mask(struct clk_hw *hw)
+{
+	struct clk *clk = hw->clk;
+	if (clk == clk32khz_ap)
+		return (1 << MAX77686_32KH_AP);
+	else if (clk == clk32khz_cp)
+		return (1 << MAX77686_32KH_CP);
+	else if (clk == clk32khz_pmic)
+		return  (1 << MAX77686_P32KH);
+	else
+		return 0;
+}
+
+static int max77686_clk_enable(struct clk_hw *hw)
+{
+	struct max77686_clk *max77686 = NULL;
+	int mask;
+
+	max77686 = get_max77686_clk(hw);
+	if (!max77686)
+		return -ENOMEM;
+	mask = max77686_clk_get_mask(hw);
+
+	return regmap_update_bits(max77686->iodev->regmap,
+				MAX77686_REG_32KHZ, mask, 1);
+}
+
+static void max77686_clk_disable(struct clk_hw *hw)
+{
+	struct max77686_clk *max77686 = NULL;
+	int mask;
+
+	max77686 = get_max77686_clk(hw);
+	if (!max77686)
+		return;
+	mask = max77686_clk_get_mask(hw);
+
+	regmap_update_bits(max77686->iodev->regmap,
+				MAX77686_REG_32KHZ, mask, 0);
+}
+
+static int max77686_clk_is_enabled(struct clk_hw *hw)
+{
+	struct max77686_clk *max77686 = NULL;
+	int mask, ret;
+	int val;
+
+	max77686 = get_max77686_clk(hw);
+	if (!max77686)
+		return -ENOMEM;
+	mask = max77686_clk_get_mask(hw);
+
+	ret = regmap_read(max77686->iodev->regmap,
+				MAX77686_REG_32KHZ, &val);
+	if (ret < 0)
+		return -EINVAL;
+
+	return val & mask;
+}
+
+static struct clk_ops max77686_clk_ops = {
+	.enable		= max77686_clk_enable,
+	.disable	= max77686_clk_disable,
+	.is_enabled	= max77686_clk_is_enabled,
+};
+
+
+static __devinit int max77686_clk_probe(struct platform_device *pdev)
+{
+	struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+	struct max77686_clk *max77686;
+	int ret;
+
+	max77686 = devm_kzalloc(&pdev->dev, sizeof(struct max77686_clk),
+				GFP_KERNEL);
+	if (!max77686)
+		return -ENOMEM;
+
+	max77686->iodev = iodev;
+
+	clk32khz_ap = clk_register(&pdev->dev, max77686_clk[MAX77686_32KH_AP],
+				&max77686_clk_ops, &max77686->clk32khz_ap_hw,
+				NULL, 0, CLK_IS_ROOT);
+	if (IS_ERR(clk32khz_ap)) {
+		ret = PTR_ERR(clk32khz_ap);
+		goto err;
+	}
+
+	clk32khz_cp = clk_register(&pdev->dev, max77686_clk[MAX77686_32KH_CP],
+				&max77686_clk_ops, &max77686->clk32khz_cp_hw,
+				NULL, 0, CLK_IS_ROOT);
+	if (IS_ERR(clk32khz_cp)) {
+		ret = PTR_ERR(clk32khz_cp);
+		goto err;
+	}
+
+	clk32khz_pmic = clk_register(&pdev->dev, max77686_clk[MAX77686_P32KH],
+				&max77686_clk_ops, &max77686->clk32khz_pmic_hw,
+				NULL, 0, CLK_IS_ROOT);
+	if (IS_ERR(clk32khz_pmic)) {
+		ret = PTR_ERR(clk32khz_pmic);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	dev_err(&pdev->dev, "Fail to register clock\n");
+	return ret;
+}
+
+static int __devexit max77686_clk_remove(struct platform_device *pdev)
+{
+	kfree(clk32khz_ap);
+	kfree(clk32khz_cp);
+	kfree(clk32khz_pmic);
+	return 0;
+}
+
+static const struct platform_device_id max77686_clk_id[] = {
+	{ "max77686-clk", 0},
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, max77686_clk_id);
+
+static struct platform_driver max77686_clk_driver = {
+	.driver = {
+		.name  = "max77686-clk",
+		.owner = THIS_MODULE,
+	},
+	.probe = max77686_clk_probe,
+	.remove = __devexit_p(max77686_clk_remove),
+	.id_table = max77686_clk_id,
+};
+
+static int __init max77686_clk_init(void)
+{
+	return platform_driver_register(&max77686_clk_driver);
+}
+subsys_initcall(max77686_clk_init);
+
+static void __init max77686_clk_cleanup(void)
+{
+	platform_driver_unregister(&max77686_clk_driver);
+}
+module_exit(max77686_clk_cleanup);
+
+MODULE_DESCRIPTION("MAXIM 77686 Clock Driver");
+MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>");
+MODULE_LICENSE("GPL");
+#endif /* CONFIG_COMMON_CLK */
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2012-09-10  1:55 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-08-28  8:54 [PATCH] clock: max77686: Add driver for Maxim 77686 32Khz crystal oscillator Jonghwa Lee
2012-09-07  0:04 ` Mike Turquette
2012-09-10  1:54   ` jonghwa3.lee
  -- strict thread matches above, loose matches on Subject: below --
2012-06-01  9:08 [PATCH] clock: max77686: Add driver for Maxim 77686 32KHz " Jonghwa Lee
2012-06-01 16:13 ` Arnd Bergmann
2012-06-01 16:21   ` Mark Brown
2012-06-02  2:50     ` Arnd Bergmann
2012-06-11  5:05   ` jonghwa3.lee

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).