Thread (31 messages) flat view 31 messages, 4 authors, 2016-01-25
STALE3893d

Revision v3 of 4 in this series.

Revisions (4)
  1. v1 [diff vs current]
  2. v2 [diff vs current]
  3. v3 current
  4. v4 [diff vs current]

[PATCH v3 04/13] clk: at91: make IRQ optional and register them later

From: Alexandre Belloni <hidden>
Date: 2015-12-04 17:13:05
Also in: linux-clk, lkml
Subsystem: common clk framework, the rest · Maintainers: Stephen Boyd, Brian Masney, Jerome Brunet, Linus Torvalds

The AT91 clock drivers make use of IRQs to avoid polling when waiting for
some clocks to be enabled. Unfortunately, this leads to a crash when those
IRQs are threaded (which happens when using preempt-rt) because they are
registered before thread creation is possible.

Use polling on those clocks until the IRQ is registered and register the
IRQ at module probe time.

Signed-off-by: Alexandre Belloni <redacted>
---
 drivers/clk/at91/clk-main.c   | 152 ++++++++++++++++++++++--------------------
 drivers/clk/at91/clk-master.c |  74 ++++++++++++++------
 drivers/clk/at91/clk-pll.c    |  75 +++++++++++++++------
 drivers/clk/at91/clk-system.c |  79 +++++++++++++++-------
 drivers/clk/at91/clk-utmi.c   |  75 ++++++++++++++-------
 drivers/clk/at91/pmc.c        | 106 ++++++++++++-----------------
 6 files changed, 337 insertions(+), 224 deletions(-)
diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c
index 5841eb958f83..4b021714e801 100644
--- a/drivers/clk/at91/clk-main.c
+++ b/drivers/clk/at91/clk-main.c
@@ -8,6 +8,7 @@
  *
  */
 
+#include <linux/clk.h>
 #include <linux/clk-provider.h>
 #include <linux/clkdev.h>
 #include <linux/clk/at91_pmc.h>
@@ -22,6 +23,8 @@
 #include <linux/regmap.h>
 #include <linux/sched.h>
 #include <linux/wait.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
 
 #include "pmc.h"
 
@@ -110,9 +113,13 @@ static int clk_main_osc_prepare(struct clk_hw *hw)
 	}
 
 	while (!clk_main_osc_ready(regmap)) {
-		enable_irq(osc->base.irq);
-		wait_event(osc->base.wait,
-			   clk_main_osc_ready(regmap));
+		if (osc->base.irq) {
+			enable_irq(osc->base.irq);
+			wait_event(osc->base.wait,
+				   clk_main_osc_ready(regmap));
+		} else {
+			cpu_relax();
+		}
 	}
 
 	return 0;
@@ -158,17 +165,15 @@ static const struct clk_ops main_osc_ops = {
 
 static struct clk * __init
 at91_clk_register_main_osc(struct regmap *regmap,
-			   unsigned int irq,
 			   const char *name,
 			   const char *parent_name,
 			   bool bypass)
 {
-	int ret;
 	struct clk_main_osc *osc;
 	struct clk *clk = NULL;
 	struct clk_init_data init;
 
-	if (!irq || !name || !parent_name)
+	if (!name || !parent_name)
 		return ERR_PTR(-EINVAL);
 
 	osc = kzalloc(sizeof(*osc), GFP_KERNEL);
@@ -183,16 +188,6 @@ at91_clk_register_main_osc(struct regmap *regmap,
 
 	osc->base.hw.init = &init;
 	osc->base.regmap = regmap;
-	osc->base.irq = irq;
-
-	init_waitqueue_head(&osc->base.wait);
-	irq_set_status_flags(irq, IRQ_NOAUTOEN);
-	ret = request_irq(irq, clk_main_irq_handler,
-			  IRQF_TRIGGER_HIGH, name, &osc->base);
-	if (ret) {
-		kfree(osc);
-		return ERR_PTR(ret);
-	}
 
 	if (bypass)
 		regmap_update_bits(regmap,
@@ -201,10 +196,8 @@ at91_clk_register_main_osc(struct regmap *regmap,
 				   AT91_PMC_OSCBYPASS | AT91_PMC_KEY);
 
 	clk = clk_register(NULL, &osc->base.hw);
-	if (IS_ERR(clk)) {
-		free_irq(irq, osc);
+	if (IS_ERR(clk))
 		kfree(osc);
-	}
 
 	return clk;
 }
@@ -212,7 +205,6 @@ at91_clk_register_main_osc(struct regmap *regmap,
 static void __init of_at91rm9200_clk_main_osc_setup(struct device_node *np)
 {
 	struct clk *clk;
-	unsigned int irq;
 	const char *name = np->name;
 	const char *parent_name;
 	struct regmap *regmap;
@@ -226,11 +218,7 @@ static void __init of_at91rm9200_clk_main_osc_setup(struct device_node *np)
 	if (IS_ERR(regmap))
 		return;
 
-	irq = irq_of_parse_and_map(np, 0);
-	if (!irq)
-		return;
-
-	clk = at91_clk_register_main_osc(regmap, irq, name, parent_name, bypass);
+	clk = at91_clk_register_main_osc(regmap, name, parent_name, bypass);
 	if (IS_ERR(clk))
 		return;
 
@@ -262,9 +250,13 @@ static int clk_main_rc_osc_prepare(struct clk_hw *hw)
 				   AT91_PMC_MOSCRCEN | AT91_PMC_KEY);
 
 	while (!clk_main_rc_osc_ready(regmap)) {
-		enable_irq(osc->base.irq);
-		wait_event(osc->base.wait,
-			   clk_main_rc_osc_ready(regmap));
+		if (osc->base.irq) {
+			enable_irq(osc->base.irq);
+			wait_event(osc->base.wait,
+				   clk_main_rc_osc_ready(regmap));
+		} else {
+			cpu_relax();
+		}
 	}
 
 	return 0;
@@ -323,11 +315,9 @@ static const struct clk_ops main_rc_osc_ops = {
 
 static struct clk * __init
 at91_clk_register_main_rc_osc(struct regmap *regmap,
-			      unsigned int irq,
 			      const char *name,
 			      u32 frequency, u32 accuracy)
 {
-	int ret;
 	struct clk_main_rc_osc *osc;
 	struct clk *clk = NULL;
 	struct clk_init_data init;
@@ -347,22 +337,12 @@ at91_clk_register_main_rc_osc(struct regmap *regmap,
 
 	osc->base.hw.init = &init;
 	osc->base.regmap = regmap;
-	osc->base.irq = irq;
 	osc->frequency = frequency;
 	osc->accuracy = accuracy;
 
-	init_waitqueue_head(&osc->base.wait);
-	irq_set_status_flags(irq, IRQ_NOAUTOEN);
-	ret = request_irq(irq, clk_main_irq_handler,
-			  IRQF_TRIGGER_HIGH, name, &osc->base);
-	if (ret)
-		return ERR_PTR(ret);
-
 	clk = clk_register(NULL, &osc->base.hw);
-	if (IS_ERR(clk)) {
-		free_irq(irq, osc);
+	if (IS_ERR(clk))
 		kfree(osc);
-	}
 
 	return clk;
 }
@@ -370,7 +350,6 @@ at91_clk_register_main_rc_osc(struct regmap *regmap,
 static void __init of_at91sam9x5_clk_main_rc_osc_setup(struct device_node *np)
 {
 	struct clk *clk;
-	unsigned int irq;
 	u32 frequency = 0;
 	u32 accuracy = 0;
 	const char *name = np->name;
@@ -380,16 +359,11 @@ static void __init of_at91sam9x5_clk_main_rc_osc_setup(struct device_node *np)
 	of_property_read_u32(np, "clock-frequency", &frequency);
 	of_property_read_u32(np, "clock-accuracy", &accuracy);
 
-	irq = irq_of_parse_and_map(np, 0);
-	if (!irq)
-		return;
-
 	regmap = syscon_node_to_regmap(of_get_parent(np));
 	if (IS_ERR(regmap))
 		return;
 
-	clk = at91_clk_register_main_rc_osc(regmap, irq, name, frequency,
-					    accuracy);
+	clk = at91_clk_register_main_rc_osc(regmap, name, frequency, accuracy);
 	if (IS_ERR(clk))
 		return;
 
@@ -536,9 +510,13 @@ static int clk_sam9x5_main_prepare(struct clk_hw *hw)
 	struct regmap *regmap = clkmain->base.regmap;
 
 	while (!clk_sam9x5_main_ready(regmap)) {
-		enable_irq(clkmain->base.irq);
-		wait_event(clkmain->base.wait,
-			   clk_sam9x5_main_ready(regmap));
+		if (clkmain->base.irq) {
+			enable_irq(clkmain->base.irq);
+			wait_event(clkmain->base.wait,
+				   clk_sam9x5_main_ready(regmap));
+		} else {
+			cpu_relax();
+		}
 	}
 
 	return clk_main_probe_frequency(regmap);
@@ -577,9 +555,13 @@ static int clk_sam9x5_main_set_parent(struct clk_hw *hw, u8 index)
 		regmap_write(regmap, AT91_CKGR_MOR, tmp & ~AT91_PMC_MOSCSEL);
 
 	while (!clk_sam9x5_main_ready(regmap)) {
-		enable_irq(clkmain->base.irq);
-		wait_event(clkmain->base.wait,
-			   clk_sam9x5_main_ready(regmap));
+		if (clkmain->base.irq) {
+			enable_irq(clkmain->base.irq);
+			wait_event(clkmain->base.wait,
+				   clk_sam9x5_main_ready(regmap));
+		} else {
+			cpu_relax();
+		}
 	}
 
 	return 0;
@@ -605,12 +587,10 @@ static const struct clk_ops sam9x5_main_ops = {
 
 static struct clk * __init
 at91_clk_register_sam9x5_main(struct regmap *regmap,
-			      unsigned int irq,
 			      const char *name,
 			      const char **parent_names,
 			      int num_parents)
 {
-	int ret;
 	struct clk_sam9x5_main *clkmain;
 	struct clk *clk = NULL;
 	struct clk_init_data init;
@@ -634,21 +614,12 @@ at91_clk_register_sam9x5_main(struct regmap *regmap,
 
 	clkmain->base.hw.init = &init;
 	clkmain->base.regmap = regmap;
-	clkmain->base.irq = irq;
 	regmap_read(clkmain->base.regmap, AT91_CKGR_MOR, &status);
 	clkmain->parent = status & AT91_PMC_MOSCEN ? 1 : 0;
-	init_waitqueue_head(&clkmain->base.wait);
-	irq_set_status_flags(irq, IRQ_NOAUTOEN);
-	ret = request_irq(irq, clk_main_irq_handler,
-			  IRQF_TRIGGER_HIGH, name, &clkmain->base);
-	if (ret)
-		return ERR_PTR(ret);
 
 	clk = clk_register(NULL, &clkmain->base.hw);
-	if (IS_ERR(clk)) {
-		free_irq(irq, clkmain);
+	if (IS_ERR(clk))
 		kfree(clkmain);
-	}
 
 	return clk;
 }
@@ -658,7 +629,6 @@ static void __init of_at91sam9x5_clk_main_setup(struct device_node *np)
 	struct clk *clk;
 	const char *parent_names[2];
 	int num_parents;
-	unsigned int irq;
 	const char *name = np->name;
 	struct regmap *regmap;
 
@@ -673,11 +643,7 @@ static void __init of_at91sam9x5_clk_main_setup(struct device_node *np)
 
 	of_property_read_string(np, "clock-output-names", &name);
 
-	irq = irq_of_parse_and_map(np, 0);
-	if (!irq)
-		return;
-
-	clk = at91_clk_register_sam9x5_main(regmap, irq, name, parent_names,
+	clk = at91_clk_register_sam9x5_main(regmap, name, parent_names,
 					    num_parents);
 	if (IS_ERR(clk))
 		return;
@@ -686,3 +652,45 @@ static void __init of_at91sam9x5_clk_main_setup(struct device_node *np)
 }
 CLK_OF_DECLARE(at91sam9x5_clk_main, "atmel,at91sam9x5-clk-main",
 	       of_at91sam9x5_clk_main_setup);
+
+static const struct of_device_id atmel_clk_main_dt_ids[] = {
+	{ .compatible = "atmel,at91rm9200-clk-main-osc" },
+	{ .compatible = "atmel,at91sam9x5-clk-main-rc-osc" },
+	{ .compatible = "atmel,at91sam9x5-clk-main" },
+	{ /* sentinel */ }
+};
+
+static int __init atmel_clk_main_probe(struct platform_device *pdev)
+{
+	struct of_phandle_args clkspec = { .np = pdev->dev.of_node};
+	struct clk_main *clkmain;
+	struct clk_hw *hw;
+	int ret;
+
+	hw = __clk_get_hw(of_clk_get_from_provider(&clkspec));
+	if (!hw)
+		return -ENODEV;
+
+	clkmain = to_clk_main(hw);
+	clkmain->irq = platform_get_irq(pdev, 0);
+	if (!clkmain->irq)
+		return 0;
+
+	init_waitqueue_head(&clkmain->wait);
+	irq_set_status_flags(clkmain->irq, IRQ_NOAUTOEN);
+	ret = devm_request_irq(&pdev->dev, clkmain->irq, clk_main_irq_handler,
+			       IRQF_TRIGGER_HIGH, __clk_get_name(hw->clk),
+			       clkmain);
+	if (ret)
+		clkmain->irq = 0;
+
+	return ret;
+}
+
+static struct platform_driver atmel_clk_main = {
+	.driver = {
+		.name = "atmel-clk-main",
+		.of_match_table = atmel_clk_main_dt_ids,
+	},
+};
+module_platform_driver_probe(atmel_clk_main, atmel_clk_main_probe);
diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c
index 8d94ddfc9c72..700d0cbf5cd4 100644
--- a/drivers/clk/at91/clk-master.c
+++ b/drivers/clk/at91/clk-master.c
@@ -8,12 +8,15 @@
  *
  */
 
+#include <linux/clk.h>
 #include <linux/clk-provider.h>
 #include <linux/clkdev.h>
 #include <linux/clk/at91_pmc.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
 #include <linux/io.h>
 #include <linux/wait.h>
 #include <linux/sched.h>
@@ -77,9 +80,13 @@ static int clk_master_prepare(struct clk_hw *hw)
 	struct clk_master *master = to_clk_master(hw);
 
 	while (!clk_master_ready(master->regmap)) {
-		enable_irq(master->irq);
-		wait_event(master->wait,
-			   clk_master_ready(master->regmap));
+		if (master->irq) {
+			enable_irq(master->irq);
+			wait_event(master->wait,
+				   clk_master_ready(master->regmap));
+		} else {
+			cpu_relax();
+		}
 	}
 
 	return 0;
@@ -143,13 +150,12 @@ static const struct clk_ops master_ops = {
 };
 
 static struct clk * __init
-at91_clk_register_master(struct regmap *regmap, unsigned int irq,
+at91_clk_register_master(struct regmap *regmap,
 		const char *name, int num_parents,
 		const char **parent_names,
 		const struct clk_master_layout *layout,
 		const struct clk_master_characteristics *characteristics)
 {
-	int ret;
 	struct clk_master *master;
 	struct clk *clk = NULL;
 	struct clk_init_data init;
@@ -171,19 +177,9 @@ at91_clk_register_master(struct regmap *regmap, unsigned int irq,
 	master->layout = layout;
 	master->characteristics = characteristics;
 	master->regmap = regmap;
-	master->irq = irq;
-	init_waitqueue_head(&master->wait);
-	irq_set_status_flags(master->irq, IRQ_NOAUTOEN);
-	ret = request_irq(master->irq, clk_master_irq_handler,
-			  IRQF_TRIGGER_HIGH, "clk-master", master);
-	if (ret) {
-		kfree(master);
-		return ERR_PTR(ret);
-	}
 
 	clk = clk_register(NULL, &master->hw);
 	if (IS_ERR(clk)) {
-		free_irq(master->irq, master);
 		kfree(master);
 	}
 
@@ -233,7 +229,6 @@ of_at91_clk_master_setup(struct device_node *np,
 {
 	struct clk *clk;
 	int num_parents;
-	unsigned int irq;
 	const char *parent_names[MASTER_SOURCE_MAX];
 	const char *name = np->name;
 	struct clk_master_characteristics *characteristics;
@@ -255,11 +250,7 @@ of_at91_clk_master_setup(struct device_node *np,
 	if (IS_ERR(regmap))
 		return;
 
-	irq = irq_of_parse_and_map(np, 0);
-	if (!irq)
-		goto out_free_characteristics;
-
-	clk = at91_clk_register_master(regmap, irq, name, num_parents,
+	clk = at91_clk_register_master(regmap, name, num_parents,
 				       parent_names, layout,
 				       characteristics);
 	if (IS_ERR(clk))
@@ -285,3 +276,44 @@ static void __init of_at91sam9x5_clk_master_setup(struct device_node *np)
 }
 CLK_OF_DECLARE(at91sam9x5_clk_master, "atmel,at91sam9x5-clk-master",
 	       of_at91sam9x5_clk_master_setup);
+
+static const struct of_device_id atmel_clk_master_dt_ids[] = {
+	{ .compatible = "atmel,at91rm9200-clk-master" },
+	{ .compatible = "atmel,at91sam9x5-clk-master" },
+	{ /* sentinel */ }
+};
+
+static int __init atmel_clk_master_probe(struct platform_device *pdev)
+{
+	struct of_phandle_args clkspec = { .np = pdev->dev.of_node};
+	struct clk_master *master;
+	struct clk_hw *hw;
+	int ret;
+
+	hw = __clk_get_hw(of_clk_get_from_provider(&clkspec));
+	if (!hw)
+		return -ENODEV;
+
+	master = to_clk_master(hw);
+	master->irq = platform_get_irq(pdev, 0);
+	if (!master->irq)
+		return 0;
+
+	init_waitqueue_head(&master->wait);
+	irq_set_status_flags(master->irq, IRQ_NOAUTOEN);
+	ret = devm_request_irq(&pdev->dev, master->irq, clk_master_irq_handler,
+			       IRQF_TRIGGER_HIGH, __clk_get_name(hw->clk),
+			       master);
+	if (ret)
+		master->irq = 0;
+
+	return ret;
+}
+
+static struct platform_driver atmel_clk_master = {
+	.driver = {
+		.name = "atmel-clk-master",
+		.of_match_table = atmel_clk_master_dt_ids,
+	},
+};
+module_platform_driver_probe(atmel_clk_master, atmel_clk_master_probe);
diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c
index 5f4c6ce628e0..0cf69865c2c8 100644
--- a/drivers/clk/at91/clk-pll.c
+++ b/drivers/clk/at91/clk-pll.c
@@ -8,6 +8,7 @@
  *
  */
 
+#include <linux/clk.h>
 #include <linux/clk-provider.h>
 #include <linux/clkdev.h>
 #include <linux/clk/at91_pmc.h>
@@ -21,6 +22,8 @@
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
 #include <linux/regmap.h>
 
 #include "pmc.h"
@@ -128,9 +131,13 @@ static int clk_pll_prepare(struct clk_hw *hw)
 			((pll->mul & layout->mul_mask) << layout->mul_shift));
 
 	while (!clk_pll_ready(regmap, pll->id)) {
-		enable_irq(pll->irq);
-		wait_event(pll->wait,
-			   clk_pll_ready(regmap, pll->id));
+		if (pll->irq) {
+			enable_irq(pll->irq);
+			wait_event(pll->wait,
+				   clk_pll_ready(regmap, pll->id));
+		} else {
+			cpu_relax();
+		}
 	}
 
 	return 0;
@@ -320,7 +327,7 @@ static const struct clk_ops pll_ops = {
 };
 
 static struct clk * __init
-at91_clk_register_pll(struct regmap *regmap, unsigned int irq, const char *name,
+at91_clk_register_pll(struct regmap *regmap, const char *name,
 		      const char *parent_name, u8 id,
 		      const struct clk_pll_layout *layout,
 		      const struct clk_pll_characteristics *characteristics)
@@ -328,7 +335,6 @@ at91_clk_register_pll(struct regmap *regmap, unsigned int irq, const char *name,
 	struct clk_pll *pll;
 	struct clk *clk = NULL;
 	struct clk_init_data init;
-	int ret;
 	int offset = PLL_REG(id);
 	unsigned int pllr;
 
@@ -350,22 +356,12 @@ at91_clk_register_pll(struct regmap *regmap, unsigned int irq, const char *name,
 	pll->layout = layout;
 	pll->characteristics = characteristics;
 	pll->regmap = regmap;
-	pll->irq = irq;
 	regmap_read(regmap, offset, &pllr);
 	pll->div = PLL_DIV(pllr);
 	pll->mul = PLL_MUL(pllr, layout);
-	init_waitqueue_head(&pll->wait);
-	irq_set_status_flags(pll->irq, IRQ_NOAUTOEN);
-	ret = request_irq(pll->irq, clk_pll_irq_handler, IRQF_TRIGGER_HIGH,
-			  id ? "clk-pllb" : "clk-plla", pll);
-	if (ret) {
-		kfree(pll);
-		return ERR_PTR(ret);
-	}
 
 	clk = clk_register(NULL, &pll->hw);
 	if (IS_ERR(clk)) {
-		free_irq(pll->irq, pll);
 		kfree(pll);
 	}
 
@@ -499,7 +495,6 @@ of_at91_clk_pll_setup(struct device_node *np,
 		      const struct clk_pll_layout *layout)
 {
 	u32 id;
-	unsigned int irq;
 	struct clk *clk;
 	struct regmap *regmap;
 	const char *parent_name;
@@ -521,11 +516,7 @@ of_at91_clk_pll_setup(struct device_node *np,
 	if (!characteristics)
 		return;
 
-	irq = irq_of_parse_and_map(np, 0);
-	if (!irq)
-		return;
-
-	clk = at91_clk_register_pll(regmap, irq, name, parent_name, id, layout,
+	clk = at91_clk_register_pll(regmap, name, parent_name, id, layout,
 				    characteristics);
 	if (IS_ERR(clk))
 		goto out_free_characteristics;
@@ -564,3 +555,45 @@ static void __init of_sama5d3_clk_pll_setup(struct device_node *np)
 }
 CLK_OF_DECLARE(sama5d3_clk_pll, "atmel,sama5d3-clk-pll",
 	       of_sama5d3_clk_pll_setup);
+
+static const struct of_device_id atmel_clk_pll_dt_ids[] = {
+	{ .compatible = "atmel,at91rm9200-clk-pll" },
+	{ .compatible = "atmel,at91sam9g45-clk-pll" },
+	{ .compatible = "atmel,at91sam9g20-clk-pllb" },
+	{ .compatible = "atmel,sama5d3-clk-pll" },
+	{ /* sentinel */ }
+};
+
+static int __init atmel_clk_pll_probe(struct platform_device *pdev)
+{
+	struct of_phandle_args clkspec = { .np = pdev->dev.of_node};
+	struct clk_pll *pll;
+	struct clk_hw *hw;
+	int ret;
+
+	hw = __clk_get_hw(of_clk_get_from_provider(&clkspec));
+	if (!hw)
+		return -ENODEV;
+
+	pll = to_clk_pll(hw);
+	pll->irq = platform_get_irq(pdev, 0);
+	if (!pll->irq)
+		return 0;
+
+	init_waitqueue_head(&pll->wait);
+	irq_set_status_flags(pll->irq, IRQ_NOAUTOEN);
+	ret = devm_request_irq(&pdev->dev, pll->irq, clk_pll_irq_handler,
+			       IRQF_TRIGGER_HIGH, __clk_get_name(hw->clk), pll);
+	if (ret)
+		pll->irq = 0;
+
+	return ret;
+}
+
+static struct platform_driver atmel_clk_pll = {
+	.driver = {
+		.name = "atmel-clk-pll",
+		.of_match_table = atmel_clk_pll_dt_ids,
+	},
+};
+module_platform_driver_probe(atmel_clk_pll, atmel_clk_pll_probe);
diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c
index 0593adf1bf4b..d5fc7bbcc6b3 100644
--- a/drivers/clk/at91/clk-system.c
+++ b/drivers/clk/at91/clk-system.c
@@ -8,6 +8,7 @@
  *
  */
 
+#include <linux/clk.h>
 #include <linux/clk-provider.h>
 #include <linux/clkdev.h>
 #include <linux/clk/at91_pmc.h>
@@ -20,6 +21,8 @@
 #include <linux/wait.h>
 #include <linux/sched.h>
 #include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
 #include <linux/regmap.h>
 
 #include "pmc.h"
@@ -41,6 +44,7 @@ static inline int is_pck(int id)
 {
 	return (id >= 8) && (id <= 15);
 }
+
 static irqreturn_t clk_system_irq_handler(int irq, void *dev_id)
 {
 	struct clk_system *sys = (struct clk_system *)dev_id;
@@ -114,12 +118,11 @@ static const struct clk_ops system_ops = {
 
 static struct clk * __init
 at91_clk_register_system(struct regmap *regmap, const char *name,
-			 const char *parent_name, u8 id, int irq)
+			 const char *parent_name, u8 id)
 {
 	struct clk_system *sys;
 	struct clk *clk = NULL;
 	struct clk_init_data init;
-	int ret;
 
 	if (!parent_name || id > SYSTEM_MAX_ID)
 		return ERR_PTR(-EINVAL);
@@ -137,24 +140,10 @@ at91_clk_register_system(struct regmap *regmap, const char *name,
 	sys->id = id;
 	sys->hw.init = &init;
 	sys->regmap = regmap;
-	sys->irq = irq;
-	if (irq) {
-		init_waitqueue_head(&sys->wait);
-		irq_set_status_flags(sys->irq, IRQ_NOAUTOEN);
-		ret = request_irq(sys->irq, clk_system_irq_handler,
-				IRQF_TRIGGER_HIGH, name, sys);
-		if (ret) {
-			kfree(sys);
-			return ERR_PTR(ret);
-		}
-	}
 
 	clk = clk_register(NULL, &sys->hw);
-	if (IS_ERR(clk)) {
-		if (irq)
-			free_irq(sys->irq, sys);
+	if (IS_ERR(clk))
 		kfree(sys);
-	}
 
 	return clk;
 }
@@ -162,7 +151,6 @@ at91_clk_register_system(struct regmap *regmap, const char *name,
 static void __init of_at91rm9200_clk_sys_setup(struct device_node *np)
 {
 	int num;
-	int irq = 0;
 	u32 id;
 	struct clk *clk;
 	const char *name;
@@ -185,13 +173,9 @@ static void __init of_at91rm9200_clk_sys_setup(struct device_node *np)
 		if (of_property_read_string(np, "clock-output-names", &name))
 			name = sysclknp->name;
 
-		if (is_pck(id))
-			irq = irq_of_parse_and_map(sysclknp, 0);
-
 		parent_name = of_clk_get_parent_name(sysclknp, 0);
 
-		clk = at91_clk_register_system(regmap, name, parent_name, id,
-					       irq);
+		clk = at91_clk_register_system(regmap, name, parent_name, id);
 		if (IS_ERR(clk))
 			continue;
 
@@ -200,3 +184,52 @@ static void __init of_at91rm9200_clk_sys_setup(struct device_node *np)
 }
 CLK_OF_DECLARE(at91rm9200_clk_sys, "atmel,at91rm9200-clk-system",
 	       of_at91rm9200_clk_sys_setup);
+
+static const struct of_device_id atmel_clk_sys_dt_ids[] = {
+	{ .compatible = "atmel,at91rm9200-clk-system" },
+	{ /* sentinel */ }
+};
+
+static int __init atmel_clk_sys_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct device_node *sysclknp;
+
+	for_each_child_of_node(np, sysclknp) {
+		struct of_phandle_args clkspec = { .np = sysclknp};
+		struct clk_hw *hw;
+		struct clk_system *sys;
+		int err;
+
+		hw = __clk_get_hw(of_clk_get_from_provider(&clkspec));
+		if (!hw)
+			continue;
+
+		sys = to_clk_system(hw);
+		if (!is_pck(sys->id))
+			continue;
+
+		sys->irq = irq_of_parse_and_map(sysclknp, 0);
+		if (!sys->irq)
+			continue;
+
+		init_waitqueue_head(&sys->wait);
+		irq_set_status_flags(sys->irq, IRQ_NOAUTOEN);
+		err = devm_request_irq(&pdev->dev, sys->irq,
+				       clk_system_irq_handler,
+				       IRQF_TRIGGER_HIGH,
+				       __clk_get_name(hw->clk), sys);
+		if (err)
+			sys->irq = 0;
+	}
+
+	return 0;
+}
+
+static struct platform_driver atmel_clk_sys = {
+	.driver = {
+		.name = "atmel-clk-sys",
+		.of_match_table = atmel_clk_sys_dt_ids,
+	},
+};
+module_platform_driver_probe(atmel_clk_sys, atmel_clk_sys_probe);
diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c
index 58a310e5768c..1f2fbf3bd13e 100644
--- a/drivers/clk/at91/clk-utmi.c
+++ b/drivers/clk/at91/clk-utmi.c
@@ -8,6 +8,7 @@
  *
  */
 
+#include <linux/clk.h>
 #include <linux/clk-provider.h>
 #include <linux/clkdev.h>
 #include <linux/clk/at91_pmc.h>
@@ -20,6 +21,8 @@
 #include <linux/sched.h>
 #include <linux/wait.h>
 #include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
 #include <linux/regmap.h>
 
 #include "pmc.h"
@@ -63,9 +66,13 @@ static int clk_utmi_prepare(struct clk_hw *hw)
 	regmap_update_bits(utmi->regmap, AT91_CKGR_UCKR, uckr, uckr);
 
 	while (!clk_utmi_ready(utmi->regmap)) {
-		enable_irq(utmi->irq);
-		wait_event(utmi->wait,
-			   clk_utmi_ready(utmi->regmap));
+		if (utmi->irq) {
+			enable_irq(utmi->irq);
+			wait_event(utmi->wait,
+				   clk_utmi_ready(utmi->regmap));
+		} else {
+			cpu_relax();
+		}
 	}
 
 	return 0;
@@ -100,10 +107,9 @@ static const struct clk_ops utmi_ops = {
 };
 
 static struct clk * __init
-at91_clk_register_utmi(struct regmap *regmap, unsigned int irq,
+at91_clk_register_utmi(struct regmap *regmap,
 		       const char *name, const char *parent_name)
 {
-	int ret;
 	struct clk_utmi *utmi;
 	struct clk *clk = NULL;
 	struct clk_init_data init;
@@ -120,28 +126,16 @@ at91_clk_register_utmi(struct regmap *regmap, unsigned int irq,
 
 	utmi->hw.init = &init;
 	utmi->regmap = regmap;
-	utmi->irq = irq;
-	init_waitqueue_head(&utmi->wait);
-	irq_set_status_flags(utmi->irq, IRQ_NOAUTOEN);
-	ret = request_irq(utmi->irq, clk_utmi_irq_handler,
-			  IRQF_TRIGGER_HIGH, "clk-utmi", utmi);
-	if (ret) {
-		kfree(utmi);
-		return ERR_PTR(ret);
-	}
 
 	clk = clk_register(NULL, &utmi->hw);
-	if (IS_ERR(clk)) {
-		free_irq(utmi->irq, utmi);
+	if (IS_ERR(clk))
 		kfree(utmi);
-	}
 
 	return clk;
 }
 
 static void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np)
 {
-	unsigned int irq;
 	struct clk *clk;
 	const char *parent_name;
 	const char *name = np->name;
@@ -151,15 +145,11 @@ static void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np)
 
 	of_property_read_string(np, "clock-output-names", &name);
 
-	irq = irq_of_parse_and_map(np, 0);
-	if (!irq)
-		return;
-
 	regmap = syscon_node_to_regmap(of_get_parent(np));
 	if (IS_ERR(regmap))
 		return;
 
-	clk = at91_clk_register_utmi(regmap, irq, name, parent_name);
+	clk = at91_clk_register_utmi(regmap, name, parent_name);
 	if (IS_ERR(clk))
 		return;
 
@@ -168,3 +158,42 @@ static void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np)
 }
 CLK_OF_DECLARE(at91sam9x5_clk_utmi, "atmel,at91sam9x5-clk-utmi",
 	       of_at91sam9x5_clk_utmi_setup);
+
+static const struct of_device_id atmel_clk_utmi_dt_ids[] = {
+	{ .compatible = "atmel,at91sam9x5-clk-utmi" },
+	{ /* sentinel */ }
+};
+
+static int __init atmel_clk_utmi_probe(struct platform_device *pdev)
+{
+	struct of_phandle_args clkspec = { .np = pdev->dev.of_node};
+	struct clk_utmi *utmi;
+	struct clk_hw *hw;
+	int ret;
+
+	hw = __clk_get_hw(of_clk_get_from_provider(&clkspec));
+	if (!hw)
+		return -ENODEV;
+
+	utmi = to_clk_utmi(hw);
+	utmi->irq = platform_get_irq(pdev, 0);
+	if (!utmi->irq)
+		return 0;
+
+	init_waitqueue_head(&utmi->wait);
+	irq_set_status_flags(utmi->irq, IRQ_NOAUTOEN);
+	ret = devm_request_irq(&pdev->dev, utmi->irq, clk_utmi_irq_handler,
+			       IRQF_TRIGGER_HIGH, __clk_get_name(hw->clk), utmi);
+	if (ret)
+		utmi->irq = 0;
+
+	return ret;
+}
+
+static struct platform_driver atmel_clk_utmi = {
+	.driver = {
+		.name = "atmel-clk-utmi",
+		.of_match_table = atmel_clk_utmi_dt_ids,
+	},
+};
+module_platform_driver_probe(atmel_clk_utmi, atmel_clk_utmi_probe);
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 295b17b9c689..296d20a29c6c 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -20,6 +20,9 @@
 #include <linux/irqdomain.h>
 #include <linux/of_irq.h>
 #include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
 #include <linux/regmap.h>
 
 #include <asm/proc-fns.h>
@@ -248,32 +251,30 @@ static struct at91_pmc *__init at91_pmc_init(struct device_node *np,
 	pmc->virq = virq;
 	pmc->caps = caps;
 
-	pmc->irqdomain = irq_domain_add_linear(np, 32, &pmc_irq_ops, pmc);
-	if (!pmc->irqdomain)
-		goto out_free_pmc;
-
-	regmap_write(pmc->regmap, AT91_PMC_IDR, 0xffffffff);
-	if (request_irq(pmc->virq, pmc_irq_handler,
-			IRQF_SHARED | IRQF_COND_SUSPEND, "pmc", pmc))
-		goto out_remove_irqdomain;
-
 	return pmc;
-
-out_remove_irqdomain:
-	irq_domain_remove(pmc->irqdomain);
-out_free_pmc:
-	kfree(pmc);
-
-	return NULL;
 }
 
-static void __init of_at91_pmc_setup(struct device_node *np,
-				     const struct at91_pmc_caps *caps)
+static const struct of_device_id atmel_pmc_dt_ids[] = {
+	{ .compatible = "atmel,at91rm9200-pmc", .data = &at91rm9200_caps },
+	{ .compatible = "atmel,at91sam9260-pmc", .data = &at91sam9260_caps },
+	{ .compatible = "atmel,at91sam9g45-pmc", .data = &at91sam9g45_caps },
+	{ .compatible = "atmel,at91sam9n12-pmc", .data = &at91sam9n12_caps },
+	{ .compatible = "atmel,at91sam9x5-pmc", .data = &at91sam9x5_caps },
+	{ .compatible = "atmel,sama5d2-pmc", .data = &sama5d2_caps },
+	{ .compatible = "atmel,sama5d3-pmc", .data = &sama5d3_caps },
+	{ /* sentinel */ },
+};
+
+static int __init atmel_pmc_probe(struct platform_device *pdev)
 {
+	const struct of_device_id *of_id;
+	const struct at91_pmc_caps *caps;
+	struct device_node *np = pdev->dev.of_node;
 	struct at91_pmc *pmc;
 	void __iomem *regbase = of_iomap(np, 0);
 	struct regmap *regmap;
 	int virq;
+	int ret = 0;
 
 	regmap = syscon_node_to_regmap(np);
 	if (IS_ERR(regmap))
@@ -281,58 +282,35 @@ static void __init of_at91_pmc_setup(struct device_node *np,
 
 	virq = irq_of_parse_and_map(np, 0);
 	if (!virq)
-		return;
+		return 0;
+
+	of_id = of_match_device(atmel_pmc_dt_ids, &pdev->dev);
+	caps = of_id->data;
 
 	pmc = at91_pmc_init(np, regmap, regbase, virq, caps);
 	if (!pmc)
-		return;
-}
-
-static void __init of_at91rm9200_pmc_setup(struct device_node *np)
-{
-	of_at91_pmc_setup(np, &at91rm9200_caps);
-}
-CLK_OF_DECLARE(at91rm9200_clk_pmc, "atmel,at91rm9200-pmc",
-	       of_at91rm9200_pmc_setup);
-
-static void __init of_at91sam9260_pmc_setup(struct device_node *np)
-{
-	of_at91_pmc_setup(np, &at91sam9260_caps);
-}
-CLK_OF_DECLARE(at91sam9260_clk_pmc, "atmel,at91sam9260-pmc",
-	       of_at91sam9260_pmc_setup);
+		return 0;
 
-static void __init of_at91sam9g45_pmc_setup(struct device_node *np)
-{
-	of_at91_pmc_setup(np, &at91sam9g45_caps);
-}
-CLK_OF_DECLARE(at91sam9g45_clk_pmc, "atmel,at91sam9g45-pmc",
-	       of_at91sam9g45_pmc_setup);
+	pmc->irqdomain = irq_domain_add_linear(pdev->dev.of_node, 32,
+					       &pmc_irq_ops, pmc);
+	if (!pmc->irqdomain)
+		return 0;
 
-static void __init of_at91sam9n12_pmc_setup(struct device_node *np)
-{
-	of_at91_pmc_setup(np, &at91sam9n12_caps);
-}
-CLK_OF_DECLARE(at91sam9n12_clk_pmc, "atmel,at91sam9n12-pmc",
-	       of_at91sam9n12_pmc_setup);
+	regmap_write(pmc->regmap, AT91_PMC_IDR, 0xffffffff);
+	ret = request_irq(pmc->virq, pmc_irq_handler,
+			  IRQF_SHARED | IRQF_COND_SUSPEND, "pmc", pmc);
+	if (ret)
+		return ret;
 
-static void __init of_at91sam9x5_pmc_setup(struct device_node *np)
-{
-	of_at91_pmc_setup(np, &at91sam9x5_caps);
-}
-CLK_OF_DECLARE(at91sam9x5_clk_pmc, "atmel,at91sam9x5-pmc",
-	       of_at91sam9x5_pmc_setup);
+	of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
 
-static void __init of_sama5d2_pmc_setup(struct device_node *np)
-{
-	of_at91_pmc_setup(np, &sama5d2_caps);
+	return 0;
 }
-CLK_OF_DECLARE(sama5d2_clk_pmc, "atmel,sama5d2-pmc",
-	       of_sama5d2_pmc_setup);
 
-static void __init of_sama5d3_pmc_setup(struct device_node *np)
-{
-	of_at91_pmc_setup(np, &sama5d3_caps);
-}
-CLK_OF_DECLARE(sama5d3_clk_pmc, "atmel,sama5d3-pmc",
-	       of_sama5d3_pmc_setup);
+static struct platform_driver atmel_pmc = {
+	.driver = {
+		.name = "atmel-pmc",
+		.of_match_table = atmel_pmc_dt_ids,
+	},
+};
+module_platform_driver_probe(atmel_pmc, atmel_pmc_probe);
-- 
2.5.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help