180eded6cSBoris BREZILLON /* 280eded6cSBoris BREZILLON * drivers/clk/at91/clk-slow.c 380eded6cSBoris BREZILLON * 480eded6cSBoris BREZILLON * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> 580eded6cSBoris BREZILLON * 680eded6cSBoris BREZILLON * This program is free software; you can redistribute it and/or modify 780eded6cSBoris BREZILLON * it under the terms of the GNU General Public License as published by 880eded6cSBoris BREZILLON * the Free Software Foundation; either version 2 of the License, or 980eded6cSBoris BREZILLON * (at your option) any later version. 1080eded6cSBoris BREZILLON * 1180eded6cSBoris BREZILLON */ 1280eded6cSBoris BREZILLON 1380eded6cSBoris BREZILLON #include <linux/clk-provider.h> 1480eded6cSBoris BREZILLON #include <linux/clkdev.h> 1580eded6cSBoris BREZILLON #include <linux/clk/at91_pmc.h> 1680eded6cSBoris BREZILLON #include <linux/of.h> 171bdf0232SBoris Brezillon #include <linux/mfd/syscon.h> 181bdf0232SBoris Brezillon #include <linux/regmap.h> 1980eded6cSBoris BREZILLON 2080eded6cSBoris BREZILLON #include "pmc.h" 2180eded6cSBoris BREZILLON 2280eded6cSBoris BREZILLON struct clk_sam9260_slow { 2380eded6cSBoris BREZILLON struct clk_hw hw; 241bdf0232SBoris Brezillon struct regmap *regmap; 2580eded6cSBoris BREZILLON }; 2680eded6cSBoris BREZILLON 2780eded6cSBoris BREZILLON #define to_clk_sam9260_slow(hw) container_of(hw, struct clk_sam9260_slow, hw) 2880eded6cSBoris BREZILLON 2980eded6cSBoris BREZILLON static u8 clk_sam9260_slow_get_parent(struct clk_hw *hw) 3080eded6cSBoris BREZILLON { 3180eded6cSBoris BREZILLON struct clk_sam9260_slow *slowck = to_clk_sam9260_slow(hw); 321bdf0232SBoris Brezillon unsigned int status; 3380eded6cSBoris BREZILLON 341bdf0232SBoris Brezillon regmap_read(slowck->regmap, AT91_PMC_SR, &status); 351bdf0232SBoris Brezillon 361bdf0232SBoris Brezillon return status & AT91_PMC_OSCSEL ? 1 : 0; 3780eded6cSBoris BREZILLON } 3880eded6cSBoris BREZILLON 3980eded6cSBoris BREZILLON static const struct clk_ops sam9260_slow_ops = { 4080eded6cSBoris BREZILLON .get_parent = clk_sam9260_slow_get_parent, 4180eded6cSBoris BREZILLON }; 4280eded6cSBoris BREZILLON 43*b2e39dc0SAlexandre Belloni struct clk_hw * __init 441bdf0232SBoris Brezillon at91_clk_register_sam9260_slow(struct regmap *regmap, 4580eded6cSBoris BREZILLON const char *name, 4680eded6cSBoris BREZILLON const char **parent_names, 4780eded6cSBoris BREZILLON int num_parents) 4880eded6cSBoris BREZILLON { 4980eded6cSBoris BREZILLON struct clk_sam9260_slow *slowck; 50f5644f10SStephen Boyd struct clk_hw *hw; 5180eded6cSBoris BREZILLON struct clk_init_data init; 52f5644f10SStephen Boyd int ret; 5380eded6cSBoris BREZILLON 541bdf0232SBoris Brezillon if (!name) 5580eded6cSBoris BREZILLON return ERR_PTR(-EINVAL); 5680eded6cSBoris BREZILLON 5780eded6cSBoris BREZILLON if (!parent_names || !num_parents) 5880eded6cSBoris BREZILLON return ERR_PTR(-EINVAL); 5980eded6cSBoris BREZILLON 6080eded6cSBoris BREZILLON slowck = kzalloc(sizeof(*slowck), GFP_KERNEL); 6180eded6cSBoris BREZILLON if (!slowck) 6280eded6cSBoris BREZILLON return ERR_PTR(-ENOMEM); 6380eded6cSBoris BREZILLON 6480eded6cSBoris BREZILLON init.name = name; 6580eded6cSBoris BREZILLON init.ops = &sam9260_slow_ops; 6680eded6cSBoris BREZILLON init.parent_names = parent_names; 6780eded6cSBoris BREZILLON init.num_parents = num_parents; 6880eded6cSBoris BREZILLON init.flags = 0; 6980eded6cSBoris BREZILLON 7080eded6cSBoris BREZILLON slowck->hw.init = &init; 711bdf0232SBoris Brezillon slowck->regmap = regmap; 7280eded6cSBoris BREZILLON 73f5644f10SStephen Boyd hw = &slowck->hw; 74f5644f10SStephen Boyd ret = clk_hw_register(NULL, &slowck->hw); 75f5644f10SStephen Boyd if (ret) { 7680eded6cSBoris BREZILLON kfree(slowck); 77f5644f10SStephen Boyd hw = ERR_PTR(ret); 78f5644f10SStephen Boyd } 7980eded6cSBoris BREZILLON 80f5644f10SStephen Boyd return hw; 8180eded6cSBoris BREZILLON } 8280eded6cSBoris BREZILLON 831bdf0232SBoris Brezillon static void __init of_at91sam9260_clk_slow_setup(struct device_node *np) 8480eded6cSBoris BREZILLON { 85f5644f10SStephen Boyd struct clk_hw *hw; 8680eded6cSBoris BREZILLON const char *parent_names[2]; 878c1b1e54SStephen Boyd unsigned int num_parents; 8880eded6cSBoris BREZILLON const char *name = np->name; 891bdf0232SBoris Brezillon struct regmap *regmap; 9080eded6cSBoris BREZILLON 9151a43be9SGeert Uytterhoeven num_parents = of_clk_get_parent_count(np); 92e8531ac8SBoris BREZILLON if (num_parents != 2) 9380eded6cSBoris BREZILLON return; 9480eded6cSBoris BREZILLON 95f0557fbeSDinh Nguyen of_clk_parent_fill(np, parent_names, num_parents); 961bdf0232SBoris Brezillon regmap = syscon_node_to_regmap(of_get_parent(np)); 971bdf0232SBoris Brezillon if (IS_ERR(regmap)) 981bdf0232SBoris Brezillon return; 9980eded6cSBoris BREZILLON 10080eded6cSBoris BREZILLON of_property_read_string(np, "clock-output-names", &name); 10180eded6cSBoris BREZILLON 102f5644f10SStephen Boyd hw = at91_clk_register_sam9260_slow(regmap, name, parent_names, 10380eded6cSBoris BREZILLON num_parents); 104f5644f10SStephen Boyd if (IS_ERR(hw)) 10580eded6cSBoris BREZILLON return; 10680eded6cSBoris BREZILLON 107f5644f10SStephen Boyd of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); 10880eded6cSBoris BREZILLON } 1091bdf0232SBoris Brezillon 1101bdf0232SBoris Brezillon CLK_OF_DECLARE(at91sam9260_clk_slow, "atmel,at91sam9260-clk-slow", 1111bdf0232SBoris Brezillon of_at91sam9260_clk_slow_setup); 112