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/delay.h> 1780eded6cSBoris BREZILLON #include <linux/of.h> 181bdf0232SBoris Brezillon #include <linux/mfd/syscon.h> 191bdf0232SBoris Brezillon #include <linux/regmap.h> 2080eded6cSBoris BREZILLON 2180eded6cSBoris BREZILLON #include "pmc.h" 2280eded6cSBoris BREZILLON #include "sckc.h" 2380eded6cSBoris BREZILLON 2480eded6cSBoris BREZILLON #define SLOW_CLOCK_FREQ 32768 2580eded6cSBoris BREZILLON #define SLOWCK_SW_CYCLES 5 2680eded6cSBoris BREZILLON #define SLOWCK_SW_TIME_USEC ((SLOWCK_SW_CYCLES * USEC_PER_SEC) / \ 2780eded6cSBoris BREZILLON SLOW_CLOCK_FREQ) 2880eded6cSBoris BREZILLON 2980eded6cSBoris BREZILLON #define AT91_SCKC_CR 0x00 3080eded6cSBoris BREZILLON #define AT91_SCKC_RCEN (1 << 0) 3180eded6cSBoris BREZILLON #define AT91_SCKC_OSC32EN (1 << 1) 3280eded6cSBoris BREZILLON #define AT91_SCKC_OSC32BYP (1 << 2) 3380eded6cSBoris BREZILLON #define AT91_SCKC_OSCSEL (1 << 3) 3480eded6cSBoris BREZILLON 3580eded6cSBoris BREZILLON struct clk_slow_osc { 3680eded6cSBoris BREZILLON struct clk_hw hw; 3780eded6cSBoris BREZILLON void __iomem *sckcr; 3880eded6cSBoris BREZILLON unsigned long startup_usec; 3980eded6cSBoris BREZILLON }; 4080eded6cSBoris BREZILLON 4180eded6cSBoris BREZILLON #define to_clk_slow_osc(hw) container_of(hw, struct clk_slow_osc, hw) 4280eded6cSBoris BREZILLON 4380eded6cSBoris BREZILLON struct clk_slow_rc_osc { 4480eded6cSBoris BREZILLON struct clk_hw hw; 4580eded6cSBoris BREZILLON void __iomem *sckcr; 4680eded6cSBoris BREZILLON unsigned long frequency; 4780eded6cSBoris BREZILLON unsigned long accuracy; 4880eded6cSBoris BREZILLON unsigned long startup_usec; 4980eded6cSBoris BREZILLON }; 5080eded6cSBoris BREZILLON 5180eded6cSBoris BREZILLON #define to_clk_slow_rc_osc(hw) container_of(hw, struct clk_slow_rc_osc, hw) 5280eded6cSBoris BREZILLON 5380eded6cSBoris BREZILLON struct clk_sam9260_slow { 5480eded6cSBoris BREZILLON struct clk_hw hw; 551bdf0232SBoris Brezillon struct regmap *regmap; 5680eded6cSBoris BREZILLON }; 5780eded6cSBoris BREZILLON 5880eded6cSBoris BREZILLON #define to_clk_sam9260_slow(hw) container_of(hw, struct clk_sam9260_slow, hw) 5980eded6cSBoris BREZILLON 6080eded6cSBoris BREZILLON struct clk_sam9x5_slow { 6180eded6cSBoris BREZILLON struct clk_hw hw; 6280eded6cSBoris BREZILLON void __iomem *sckcr; 6380eded6cSBoris BREZILLON u8 parent; 6480eded6cSBoris BREZILLON }; 6580eded6cSBoris BREZILLON 6680eded6cSBoris BREZILLON #define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, hw) 6780eded6cSBoris BREZILLON 6880eded6cSBoris BREZILLON static int clk_slow_osc_prepare(struct clk_hw *hw) 6980eded6cSBoris BREZILLON { 7080eded6cSBoris BREZILLON struct clk_slow_osc *osc = to_clk_slow_osc(hw); 7180eded6cSBoris BREZILLON void __iomem *sckcr = osc->sckcr; 7280eded6cSBoris BREZILLON u32 tmp = readl(sckcr); 7380eded6cSBoris BREZILLON 7480eded6cSBoris BREZILLON if (tmp & AT91_SCKC_OSC32BYP) 7580eded6cSBoris BREZILLON return 0; 7680eded6cSBoris BREZILLON 7780eded6cSBoris BREZILLON writel(tmp | AT91_SCKC_OSC32EN, sckcr); 7880eded6cSBoris BREZILLON 7980eded6cSBoris BREZILLON usleep_range(osc->startup_usec, osc->startup_usec + 1); 8080eded6cSBoris BREZILLON 8180eded6cSBoris BREZILLON return 0; 8280eded6cSBoris BREZILLON } 8380eded6cSBoris BREZILLON 8480eded6cSBoris BREZILLON static void clk_slow_osc_unprepare(struct clk_hw *hw) 8580eded6cSBoris BREZILLON { 8680eded6cSBoris BREZILLON struct clk_slow_osc *osc = to_clk_slow_osc(hw); 8780eded6cSBoris BREZILLON void __iomem *sckcr = osc->sckcr; 8880eded6cSBoris BREZILLON u32 tmp = readl(sckcr); 8980eded6cSBoris BREZILLON 9080eded6cSBoris BREZILLON if (tmp & AT91_SCKC_OSC32BYP) 9180eded6cSBoris BREZILLON return; 9280eded6cSBoris BREZILLON 9380eded6cSBoris BREZILLON writel(tmp & ~AT91_SCKC_OSC32EN, sckcr); 9480eded6cSBoris BREZILLON } 9580eded6cSBoris BREZILLON 9680eded6cSBoris BREZILLON static int clk_slow_osc_is_prepared(struct clk_hw *hw) 9780eded6cSBoris BREZILLON { 9880eded6cSBoris BREZILLON struct clk_slow_osc *osc = to_clk_slow_osc(hw); 9980eded6cSBoris BREZILLON void __iomem *sckcr = osc->sckcr; 10080eded6cSBoris BREZILLON u32 tmp = readl(sckcr); 10180eded6cSBoris BREZILLON 10280eded6cSBoris BREZILLON if (tmp & AT91_SCKC_OSC32BYP) 10380eded6cSBoris BREZILLON return 1; 10480eded6cSBoris BREZILLON 10580eded6cSBoris BREZILLON return !!(tmp & AT91_SCKC_OSC32EN); 10680eded6cSBoris BREZILLON } 10780eded6cSBoris BREZILLON 10880eded6cSBoris BREZILLON static const struct clk_ops slow_osc_ops = { 10980eded6cSBoris BREZILLON .prepare = clk_slow_osc_prepare, 11080eded6cSBoris BREZILLON .unprepare = clk_slow_osc_unprepare, 11180eded6cSBoris BREZILLON .is_prepared = clk_slow_osc_is_prepared, 11280eded6cSBoris BREZILLON }; 11380eded6cSBoris BREZILLON 114*f5644f10SStephen Boyd static struct clk_hw * __init 11580eded6cSBoris BREZILLON at91_clk_register_slow_osc(void __iomem *sckcr, 11680eded6cSBoris BREZILLON const char *name, 11780eded6cSBoris BREZILLON const char *parent_name, 11880eded6cSBoris BREZILLON unsigned long startup, 11980eded6cSBoris BREZILLON bool bypass) 12080eded6cSBoris BREZILLON { 12180eded6cSBoris BREZILLON struct clk_slow_osc *osc; 122*f5644f10SStephen Boyd struct clk_hw *hw; 12380eded6cSBoris BREZILLON struct clk_init_data init; 124*f5644f10SStephen Boyd int ret; 12580eded6cSBoris BREZILLON 12680eded6cSBoris BREZILLON if (!sckcr || !name || !parent_name) 12780eded6cSBoris BREZILLON return ERR_PTR(-EINVAL); 12880eded6cSBoris BREZILLON 12980eded6cSBoris BREZILLON osc = kzalloc(sizeof(*osc), GFP_KERNEL); 13080eded6cSBoris BREZILLON if (!osc) 13180eded6cSBoris BREZILLON return ERR_PTR(-ENOMEM); 13280eded6cSBoris BREZILLON 13380eded6cSBoris BREZILLON init.name = name; 13480eded6cSBoris BREZILLON init.ops = &slow_osc_ops; 13580eded6cSBoris BREZILLON init.parent_names = &parent_name; 13680eded6cSBoris BREZILLON init.num_parents = 1; 13780eded6cSBoris BREZILLON init.flags = CLK_IGNORE_UNUSED; 13880eded6cSBoris BREZILLON 13980eded6cSBoris BREZILLON osc->hw.init = &init; 14080eded6cSBoris BREZILLON osc->sckcr = sckcr; 14180eded6cSBoris BREZILLON osc->startup_usec = startup; 14280eded6cSBoris BREZILLON 14380eded6cSBoris BREZILLON if (bypass) 14480eded6cSBoris BREZILLON writel((readl(sckcr) & ~AT91_SCKC_OSC32EN) | AT91_SCKC_OSC32BYP, 14580eded6cSBoris BREZILLON sckcr); 14680eded6cSBoris BREZILLON 147*f5644f10SStephen Boyd hw = &osc->hw; 148*f5644f10SStephen Boyd ret = clk_hw_register(NULL, &osc->hw); 149*f5644f10SStephen Boyd if (ret) { 15080eded6cSBoris BREZILLON kfree(osc); 151*f5644f10SStephen Boyd hw = ERR_PTR(ret); 152*f5644f10SStephen Boyd } 15380eded6cSBoris BREZILLON 154*f5644f10SStephen Boyd return hw; 15580eded6cSBoris BREZILLON } 15680eded6cSBoris BREZILLON 15780eded6cSBoris BREZILLON void __init of_at91sam9x5_clk_slow_osc_setup(struct device_node *np, 15880eded6cSBoris BREZILLON void __iomem *sckcr) 15980eded6cSBoris BREZILLON { 160*f5644f10SStephen Boyd struct clk_hw *hw; 16180eded6cSBoris BREZILLON const char *parent_name; 16280eded6cSBoris BREZILLON const char *name = np->name; 16380eded6cSBoris BREZILLON u32 startup; 16480eded6cSBoris BREZILLON bool bypass; 16580eded6cSBoris BREZILLON 16680eded6cSBoris BREZILLON parent_name = of_clk_get_parent_name(np, 0); 16780eded6cSBoris BREZILLON of_property_read_string(np, "clock-output-names", &name); 16880eded6cSBoris BREZILLON of_property_read_u32(np, "atmel,startup-time-usec", &startup); 16980eded6cSBoris BREZILLON bypass = of_property_read_bool(np, "atmel,osc-bypass"); 17080eded6cSBoris BREZILLON 171*f5644f10SStephen Boyd hw = at91_clk_register_slow_osc(sckcr, name, parent_name, startup, 17280eded6cSBoris BREZILLON bypass); 173*f5644f10SStephen Boyd if (IS_ERR(hw)) 17480eded6cSBoris BREZILLON return; 17580eded6cSBoris BREZILLON 176*f5644f10SStephen Boyd of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); 17780eded6cSBoris BREZILLON } 17880eded6cSBoris BREZILLON 17980eded6cSBoris BREZILLON static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw, 18080eded6cSBoris BREZILLON unsigned long parent_rate) 18180eded6cSBoris BREZILLON { 18280eded6cSBoris BREZILLON struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 18380eded6cSBoris BREZILLON 18480eded6cSBoris BREZILLON return osc->frequency; 18580eded6cSBoris BREZILLON } 18680eded6cSBoris BREZILLON 18780eded6cSBoris BREZILLON static unsigned long clk_slow_rc_osc_recalc_accuracy(struct clk_hw *hw, 18880eded6cSBoris BREZILLON unsigned long parent_acc) 18980eded6cSBoris BREZILLON { 19080eded6cSBoris BREZILLON struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 19180eded6cSBoris BREZILLON 19280eded6cSBoris BREZILLON return osc->accuracy; 19380eded6cSBoris BREZILLON } 19480eded6cSBoris BREZILLON 19580eded6cSBoris BREZILLON static int clk_slow_rc_osc_prepare(struct clk_hw *hw) 19680eded6cSBoris BREZILLON { 19780eded6cSBoris BREZILLON struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 19880eded6cSBoris BREZILLON void __iomem *sckcr = osc->sckcr; 19980eded6cSBoris BREZILLON 20080eded6cSBoris BREZILLON writel(readl(sckcr) | AT91_SCKC_RCEN, sckcr); 20180eded6cSBoris BREZILLON 20280eded6cSBoris BREZILLON usleep_range(osc->startup_usec, osc->startup_usec + 1); 20380eded6cSBoris BREZILLON 20480eded6cSBoris BREZILLON return 0; 20580eded6cSBoris BREZILLON } 20680eded6cSBoris BREZILLON 20780eded6cSBoris BREZILLON static void clk_slow_rc_osc_unprepare(struct clk_hw *hw) 20880eded6cSBoris BREZILLON { 20980eded6cSBoris BREZILLON struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 21080eded6cSBoris BREZILLON void __iomem *sckcr = osc->sckcr; 21180eded6cSBoris BREZILLON 21280eded6cSBoris BREZILLON writel(readl(sckcr) & ~AT91_SCKC_RCEN, sckcr); 21380eded6cSBoris BREZILLON } 21480eded6cSBoris BREZILLON 21580eded6cSBoris BREZILLON static int clk_slow_rc_osc_is_prepared(struct clk_hw *hw) 21680eded6cSBoris BREZILLON { 21780eded6cSBoris BREZILLON struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 21880eded6cSBoris BREZILLON 21980eded6cSBoris BREZILLON return !!(readl(osc->sckcr) & AT91_SCKC_RCEN); 22080eded6cSBoris BREZILLON } 22180eded6cSBoris BREZILLON 22280eded6cSBoris BREZILLON static const struct clk_ops slow_rc_osc_ops = { 22380eded6cSBoris BREZILLON .prepare = clk_slow_rc_osc_prepare, 22480eded6cSBoris BREZILLON .unprepare = clk_slow_rc_osc_unprepare, 22580eded6cSBoris BREZILLON .is_prepared = clk_slow_rc_osc_is_prepared, 22680eded6cSBoris BREZILLON .recalc_rate = clk_slow_rc_osc_recalc_rate, 22780eded6cSBoris BREZILLON .recalc_accuracy = clk_slow_rc_osc_recalc_accuracy, 22880eded6cSBoris BREZILLON }; 22980eded6cSBoris BREZILLON 230*f5644f10SStephen Boyd static struct clk_hw * __init 23180eded6cSBoris BREZILLON at91_clk_register_slow_rc_osc(void __iomem *sckcr, 23280eded6cSBoris BREZILLON const char *name, 23380eded6cSBoris BREZILLON unsigned long frequency, 23480eded6cSBoris BREZILLON unsigned long accuracy, 23580eded6cSBoris BREZILLON unsigned long startup) 23680eded6cSBoris BREZILLON { 23780eded6cSBoris BREZILLON struct clk_slow_rc_osc *osc; 238*f5644f10SStephen Boyd struct clk_hw *hw; 23980eded6cSBoris BREZILLON struct clk_init_data init; 240*f5644f10SStephen Boyd int ret; 24180eded6cSBoris BREZILLON 24280eded6cSBoris BREZILLON if (!sckcr || !name) 24380eded6cSBoris BREZILLON return ERR_PTR(-EINVAL); 24480eded6cSBoris BREZILLON 24580eded6cSBoris BREZILLON osc = kzalloc(sizeof(*osc), GFP_KERNEL); 24680eded6cSBoris BREZILLON if (!osc) 24780eded6cSBoris BREZILLON return ERR_PTR(-ENOMEM); 24880eded6cSBoris BREZILLON 24980eded6cSBoris BREZILLON init.name = name; 25080eded6cSBoris BREZILLON init.ops = &slow_rc_osc_ops; 25180eded6cSBoris BREZILLON init.parent_names = NULL; 25280eded6cSBoris BREZILLON init.num_parents = 0; 253a9bb2ef7SStephen Boyd init.flags = CLK_IGNORE_UNUSED; 25480eded6cSBoris BREZILLON 25580eded6cSBoris BREZILLON osc->hw.init = &init; 25680eded6cSBoris BREZILLON osc->sckcr = sckcr; 25780eded6cSBoris BREZILLON osc->frequency = frequency; 25880eded6cSBoris BREZILLON osc->accuracy = accuracy; 25980eded6cSBoris BREZILLON osc->startup_usec = startup; 26080eded6cSBoris BREZILLON 261*f5644f10SStephen Boyd hw = &osc->hw; 262*f5644f10SStephen Boyd ret = clk_hw_register(NULL, &osc->hw); 263*f5644f10SStephen Boyd if (ret) { 26480eded6cSBoris BREZILLON kfree(osc); 265*f5644f10SStephen Boyd hw = ERR_PTR(ret); 266*f5644f10SStephen Boyd } 26780eded6cSBoris BREZILLON 268*f5644f10SStephen Boyd return hw; 26980eded6cSBoris BREZILLON } 27080eded6cSBoris BREZILLON 27180eded6cSBoris BREZILLON void __init of_at91sam9x5_clk_slow_rc_osc_setup(struct device_node *np, 27280eded6cSBoris BREZILLON void __iomem *sckcr) 27380eded6cSBoris BREZILLON { 274*f5644f10SStephen Boyd struct clk_hw *hw; 27580eded6cSBoris BREZILLON u32 frequency = 0; 27680eded6cSBoris BREZILLON u32 accuracy = 0; 27780eded6cSBoris BREZILLON u32 startup = 0; 27880eded6cSBoris BREZILLON const char *name = np->name; 27980eded6cSBoris BREZILLON 28080eded6cSBoris BREZILLON of_property_read_string(np, "clock-output-names", &name); 28180eded6cSBoris BREZILLON of_property_read_u32(np, "clock-frequency", &frequency); 28280eded6cSBoris BREZILLON of_property_read_u32(np, "clock-accuracy", &accuracy); 28380eded6cSBoris BREZILLON of_property_read_u32(np, "atmel,startup-time-usec", &startup); 28480eded6cSBoris BREZILLON 285*f5644f10SStephen Boyd hw = at91_clk_register_slow_rc_osc(sckcr, name, frequency, accuracy, 28680eded6cSBoris BREZILLON startup); 287*f5644f10SStephen Boyd if (IS_ERR(hw)) 28880eded6cSBoris BREZILLON return; 28980eded6cSBoris BREZILLON 290*f5644f10SStephen Boyd of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); 29180eded6cSBoris BREZILLON } 29280eded6cSBoris BREZILLON 29380eded6cSBoris BREZILLON static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index) 29480eded6cSBoris BREZILLON { 29580eded6cSBoris BREZILLON struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); 29680eded6cSBoris BREZILLON void __iomem *sckcr = slowck->sckcr; 29780eded6cSBoris BREZILLON u32 tmp; 29880eded6cSBoris BREZILLON 29980eded6cSBoris BREZILLON if (index > 1) 30080eded6cSBoris BREZILLON return -EINVAL; 30180eded6cSBoris BREZILLON 30280eded6cSBoris BREZILLON tmp = readl(sckcr); 30380eded6cSBoris BREZILLON 30480eded6cSBoris BREZILLON if ((!index && !(tmp & AT91_SCKC_OSCSEL)) || 30580eded6cSBoris BREZILLON (index && (tmp & AT91_SCKC_OSCSEL))) 30680eded6cSBoris BREZILLON return 0; 30780eded6cSBoris BREZILLON 30880eded6cSBoris BREZILLON if (index) 30980eded6cSBoris BREZILLON tmp |= AT91_SCKC_OSCSEL; 31080eded6cSBoris BREZILLON else 31180eded6cSBoris BREZILLON tmp &= ~AT91_SCKC_OSCSEL; 31280eded6cSBoris BREZILLON 31380eded6cSBoris BREZILLON writel(tmp, sckcr); 31480eded6cSBoris BREZILLON 31580eded6cSBoris BREZILLON usleep_range(SLOWCK_SW_TIME_USEC, SLOWCK_SW_TIME_USEC + 1); 31680eded6cSBoris BREZILLON 31780eded6cSBoris BREZILLON return 0; 31880eded6cSBoris BREZILLON } 31980eded6cSBoris BREZILLON 32080eded6cSBoris BREZILLON static u8 clk_sam9x5_slow_get_parent(struct clk_hw *hw) 32180eded6cSBoris BREZILLON { 32280eded6cSBoris BREZILLON struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); 32380eded6cSBoris BREZILLON 32480eded6cSBoris BREZILLON return !!(readl(slowck->sckcr) & AT91_SCKC_OSCSEL); 32580eded6cSBoris BREZILLON } 32680eded6cSBoris BREZILLON 32780eded6cSBoris BREZILLON static const struct clk_ops sam9x5_slow_ops = { 32880eded6cSBoris BREZILLON .set_parent = clk_sam9x5_slow_set_parent, 32980eded6cSBoris BREZILLON .get_parent = clk_sam9x5_slow_get_parent, 33080eded6cSBoris BREZILLON }; 33180eded6cSBoris BREZILLON 332*f5644f10SStephen Boyd static struct clk_hw * __init 33380eded6cSBoris BREZILLON at91_clk_register_sam9x5_slow(void __iomem *sckcr, 33480eded6cSBoris BREZILLON const char *name, 33580eded6cSBoris BREZILLON const char **parent_names, 33680eded6cSBoris BREZILLON int num_parents) 33780eded6cSBoris BREZILLON { 33880eded6cSBoris BREZILLON struct clk_sam9x5_slow *slowck; 339*f5644f10SStephen Boyd struct clk_hw *hw; 34080eded6cSBoris BREZILLON struct clk_init_data init; 341*f5644f10SStephen Boyd int ret; 34280eded6cSBoris BREZILLON 34380eded6cSBoris BREZILLON if (!sckcr || !name || !parent_names || !num_parents) 34480eded6cSBoris BREZILLON return ERR_PTR(-EINVAL); 34580eded6cSBoris BREZILLON 34680eded6cSBoris BREZILLON slowck = kzalloc(sizeof(*slowck), GFP_KERNEL); 34780eded6cSBoris BREZILLON if (!slowck) 34880eded6cSBoris BREZILLON return ERR_PTR(-ENOMEM); 34980eded6cSBoris BREZILLON 35080eded6cSBoris BREZILLON init.name = name; 35180eded6cSBoris BREZILLON init.ops = &sam9x5_slow_ops; 35280eded6cSBoris BREZILLON init.parent_names = parent_names; 35380eded6cSBoris BREZILLON init.num_parents = num_parents; 35480eded6cSBoris BREZILLON init.flags = 0; 35580eded6cSBoris BREZILLON 35680eded6cSBoris BREZILLON slowck->hw.init = &init; 35780eded6cSBoris BREZILLON slowck->sckcr = sckcr; 35880eded6cSBoris BREZILLON slowck->parent = !!(readl(sckcr) & AT91_SCKC_OSCSEL); 35980eded6cSBoris BREZILLON 360*f5644f10SStephen Boyd hw = &slowck->hw; 361*f5644f10SStephen Boyd ret = clk_hw_register(NULL, &slowck->hw); 362*f5644f10SStephen Boyd if (ret) { 36380eded6cSBoris BREZILLON kfree(slowck); 364*f5644f10SStephen Boyd hw = ERR_PTR(ret); 365*f5644f10SStephen Boyd } 36680eded6cSBoris BREZILLON 367*f5644f10SStephen Boyd return hw; 36880eded6cSBoris BREZILLON } 36980eded6cSBoris BREZILLON 37080eded6cSBoris BREZILLON void __init of_at91sam9x5_clk_slow_setup(struct device_node *np, 37180eded6cSBoris BREZILLON void __iomem *sckcr) 37280eded6cSBoris BREZILLON { 373*f5644f10SStephen Boyd struct clk_hw *hw; 37480eded6cSBoris BREZILLON const char *parent_names[2]; 3758c1b1e54SStephen Boyd unsigned int num_parents; 37680eded6cSBoris BREZILLON const char *name = np->name; 37780eded6cSBoris BREZILLON 37851a43be9SGeert Uytterhoeven num_parents = of_clk_get_parent_count(np); 3798c1b1e54SStephen Boyd if (num_parents == 0 || num_parents > 2) 38080eded6cSBoris BREZILLON return; 38180eded6cSBoris BREZILLON 382f0557fbeSDinh Nguyen of_clk_parent_fill(np, parent_names, num_parents); 38380eded6cSBoris BREZILLON 38480eded6cSBoris BREZILLON of_property_read_string(np, "clock-output-names", &name); 38580eded6cSBoris BREZILLON 386*f5644f10SStephen Boyd hw = at91_clk_register_sam9x5_slow(sckcr, name, parent_names, 38780eded6cSBoris BREZILLON num_parents); 388*f5644f10SStephen Boyd if (IS_ERR(hw)) 38980eded6cSBoris BREZILLON return; 39080eded6cSBoris BREZILLON 391*f5644f10SStephen Boyd of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); 39280eded6cSBoris BREZILLON } 39380eded6cSBoris BREZILLON 39480eded6cSBoris BREZILLON static u8 clk_sam9260_slow_get_parent(struct clk_hw *hw) 39580eded6cSBoris BREZILLON { 39680eded6cSBoris BREZILLON struct clk_sam9260_slow *slowck = to_clk_sam9260_slow(hw); 3971bdf0232SBoris Brezillon unsigned int status; 39880eded6cSBoris BREZILLON 3991bdf0232SBoris Brezillon regmap_read(slowck->regmap, AT91_PMC_SR, &status); 4001bdf0232SBoris Brezillon 4011bdf0232SBoris Brezillon return status & AT91_PMC_OSCSEL ? 1 : 0; 40280eded6cSBoris BREZILLON } 40380eded6cSBoris BREZILLON 40480eded6cSBoris BREZILLON static const struct clk_ops sam9260_slow_ops = { 40580eded6cSBoris BREZILLON .get_parent = clk_sam9260_slow_get_parent, 40680eded6cSBoris BREZILLON }; 40780eded6cSBoris BREZILLON 408*f5644f10SStephen Boyd static struct clk_hw * __init 4091bdf0232SBoris Brezillon at91_clk_register_sam9260_slow(struct regmap *regmap, 41080eded6cSBoris BREZILLON const char *name, 41180eded6cSBoris BREZILLON const char **parent_names, 41280eded6cSBoris BREZILLON int num_parents) 41380eded6cSBoris BREZILLON { 41480eded6cSBoris BREZILLON struct clk_sam9260_slow *slowck; 415*f5644f10SStephen Boyd struct clk_hw *hw; 41680eded6cSBoris BREZILLON struct clk_init_data init; 417*f5644f10SStephen Boyd int ret; 41880eded6cSBoris BREZILLON 4191bdf0232SBoris Brezillon if (!name) 42080eded6cSBoris BREZILLON return ERR_PTR(-EINVAL); 42180eded6cSBoris BREZILLON 42280eded6cSBoris BREZILLON if (!parent_names || !num_parents) 42380eded6cSBoris BREZILLON return ERR_PTR(-EINVAL); 42480eded6cSBoris BREZILLON 42580eded6cSBoris BREZILLON slowck = kzalloc(sizeof(*slowck), GFP_KERNEL); 42680eded6cSBoris BREZILLON if (!slowck) 42780eded6cSBoris BREZILLON return ERR_PTR(-ENOMEM); 42880eded6cSBoris BREZILLON 42980eded6cSBoris BREZILLON init.name = name; 43080eded6cSBoris BREZILLON init.ops = &sam9260_slow_ops; 43180eded6cSBoris BREZILLON init.parent_names = parent_names; 43280eded6cSBoris BREZILLON init.num_parents = num_parents; 43380eded6cSBoris BREZILLON init.flags = 0; 43480eded6cSBoris BREZILLON 43580eded6cSBoris BREZILLON slowck->hw.init = &init; 4361bdf0232SBoris Brezillon slowck->regmap = regmap; 43780eded6cSBoris BREZILLON 438*f5644f10SStephen Boyd hw = &slowck->hw; 439*f5644f10SStephen Boyd ret = clk_hw_register(NULL, &slowck->hw); 440*f5644f10SStephen Boyd if (ret) { 44180eded6cSBoris BREZILLON kfree(slowck); 442*f5644f10SStephen Boyd hw = ERR_PTR(ret); 443*f5644f10SStephen Boyd } 44480eded6cSBoris BREZILLON 445*f5644f10SStephen Boyd return hw; 44680eded6cSBoris BREZILLON } 44780eded6cSBoris BREZILLON 4481bdf0232SBoris Brezillon static void __init of_at91sam9260_clk_slow_setup(struct device_node *np) 44980eded6cSBoris BREZILLON { 450*f5644f10SStephen Boyd struct clk_hw *hw; 45180eded6cSBoris BREZILLON const char *parent_names[2]; 4528c1b1e54SStephen Boyd unsigned int num_parents; 45380eded6cSBoris BREZILLON const char *name = np->name; 4541bdf0232SBoris Brezillon struct regmap *regmap; 45580eded6cSBoris BREZILLON 45651a43be9SGeert Uytterhoeven num_parents = of_clk_get_parent_count(np); 457e8531ac8SBoris BREZILLON if (num_parents != 2) 45880eded6cSBoris BREZILLON return; 45980eded6cSBoris BREZILLON 460f0557fbeSDinh Nguyen of_clk_parent_fill(np, parent_names, num_parents); 4611bdf0232SBoris Brezillon regmap = syscon_node_to_regmap(of_get_parent(np)); 4621bdf0232SBoris Brezillon if (IS_ERR(regmap)) 4631bdf0232SBoris Brezillon return; 46480eded6cSBoris BREZILLON 46580eded6cSBoris BREZILLON of_property_read_string(np, "clock-output-names", &name); 46680eded6cSBoris BREZILLON 467*f5644f10SStephen Boyd hw = at91_clk_register_sam9260_slow(regmap, name, parent_names, 46880eded6cSBoris BREZILLON num_parents); 469*f5644f10SStephen Boyd if (IS_ERR(hw)) 47080eded6cSBoris BREZILLON return; 47180eded6cSBoris BREZILLON 472*f5644f10SStephen Boyd of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); 47380eded6cSBoris BREZILLON } 4741bdf0232SBoris Brezillon 4751bdf0232SBoris Brezillon CLK_OF_DECLARE(at91sam9260_clk_slow, "atmel,at91sam9260-clk-slow", 4761bdf0232SBoris Brezillon of_at91sam9260_clk_slow_setup); 477