12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 280eded6cSBoris BREZILLON /* 380eded6cSBoris BREZILLON * drivers/clk/at91/sckc.c 480eded6cSBoris BREZILLON * 580eded6cSBoris BREZILLON * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> 680eded6cSBoris BREZILLON */ 780eded6cSBoris BREZILLON 880eded6cSBoris BREZILLON #include <linux/clk-provider.h> 980eded6cSBoris BREZILLON #include <linux/clkdev.h> 10ec187ef0SAlexandre Belloni #include <linux/delay.h> 1180eded6cSBoris BREZILLON #include <linux/of.h> 1280eded6cSBoris BREZILLON #include <linux/of_address.h> 1380eded6cSBoris BREZILLON #include <linux/io.h> 1480eded6cSBoris BREZILLON 15ec187ef0SAlexandre Belloni #define SLOW_CLOCK_FREQ 32768 16ec187ef0SAlexandre Belloni #define SLOWCK_SW_CYCLES 5 17ec187ef0SAlexandre Belloni #define SLOWCK_SW_TIME_USEC ((SLOWCK_SW_CYCLES * USEC_PER_SEC) / \ 18ec187ef0SAlexandre Belloni SLOW_CLOCK_FREQ) 19ec187ef0SAlexandre Belloni 20ec187ef0SAlexandre Belloni #define AT91_SCKC_CR 0x00 21abaceffcSClaudiu Beznea 22abaceffcSClaudiu Beznea struct clk_slow_bits { 23abaceffcSClaudiu Beznea u32 cr_rcen; 24abaceffcSClaudiu Beznea u32 cr_osc32en; 25abaceffcSClaudiu Beznea u32 cr_osc32byp; 26abaceffcSClaudiu Beznea u32 cr_oscsel; 27abaceffcSClaudiu Beznea }; 28ec187ef0SAlexandre Belloni 29ec187ef0SAlexandre Belloni struct clk_slow_osc { 30ec187ef0SAlexandre Belloni struct clk_hw hw; 31ec187ef0SAlexandre Belloni void __iomem *sckcr; 32abaceffcSClaudiu Beznea const struct clk_slow_bits *bits; 33ec187ef0SAlexandre Belloni unsigned long startup_usec; 34ec187ef0SAlexandre Belloni }; 35ec187ef0SAlexandre Belloni 36ec187ef0SAlexandre Belloni #define to_clk_slow_osc(hw) container_of(hw, struct clk_slow_osc, hw) 37ec187ef0SAlexandre Belloni 384b13b645SAlexandre Belloni struct clk_sama5d4_slow_osc { 394b13b645SAlexandre Belloni struct clk_hw hw; 404b13b645SAlexandre Belloni void __iomem *sckcr; 41abaceffcSClaudiu Beznea const struct clk_slow_bits *bits; 424b13b645SAlexandre Belloni unsigned long startup_usec; 434b13b645SAlexandre Belloni bool prepared; 444b13b645SAlexandre Belloni }; 454b13b645SAlexandre Belloni 464b13b645SAlexandre Belloni #define to_clk_sama5d4_slow_osc(hw) container_of(hw, struct clk_sama5d4_slow_osc, hw) 474b13b645SAlexandre Belloni 48ec187ef0SAlexandre Belloni struct clk_slow_rc_osc { 49ec187ef0SAlexandre Belloni struct clk_hw hw; 50ec187ef0SAlexandre Belloni void __iomem *sckcr; 51abaceffcSClaudiu Beznea const struct clk_slow_bits *bits; 52ec187ef0SAlexandre Belloni unsigned long frequency; 53ec187ef0SAlexandre Belloni unsigned long accuracy; 54ec187ef0SAlexandre Belloni unsigned long startup_usec; 55ec187ef0SAlexandre Belloni }; 56ec187ef0SAlexandre Belloni 57ec187ef0SAlexandre Belloni #define to_clk_slow_rc_osc(hw) container_of(hw, struct clk_slow_rc_osc, hw) 58ec187ef0SAlexandre Belloni 59ec187ef0SAlexandre Belloni struct clk_sam9x5_slow { 60ec187ef0SAlexandre Belloni struct clk_hw hw; 61ec187ef0SAlexandre Belloni void __iomem *sckcr; 62abaceffcSClaudiu Beznea const struct clk_slow_bits *bits; 63ec187ef0SAlexandre Belloni u8 parent; 64ec187ef0SAlexandre Belloni }; 65ec187ef0SAlexandre Belloni 66ec187ef0SAlexandre Belloni #define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, hw) 67ec187ef0SAlexandre Belloni 68ec187ef0SAlexandre Belloni static int clk_slow_osc_prepare(struct clk_hw *hw) 69ec187ef0SAlexandre Belloni { 70ec187ef0SAlexandre Belloni struct clk_slow_osc *osc = to_clk_slow_osc(hw); 71ec187ef0SAlexandre Belloni void __iomem *sckcr = osc->sckcr; 72ec187ef0SAlexandre Belloni u32 tmp = readl(sckcr); 73ec187ef0SAlexandre Belloni 74abaceffcSClaudiu Beznea if (tmp & (osc->bits->cr_osc32byp | osc->bits->cr_osc32en)) 75ec187ef0SAlexandre Belloni return 0; 76ec187ef0SAlexandre Belloni 77abaceffcSClaudiu Beznea writel(tmp | osc->bits->cr_osc32en, sckcr); 78ec187ef0SAlexandre Belloni 79ec187ef0SAlexandre Belloni usleep_range(osc->startup_usec, osc->startup_usec + 1); 80ec187ef0SAlexandre Belloni 81ec187ef0SAlexandre Belloni return 0; 82ec187ef0SAlexandre Belloni } 83ec187ef0SAlexandre Belloni 84ec187ef0SAlexandre Belloni static void clk_slow_osc_unprepare(struct clk_hw *hw) 85ec187ef0SAlexandre Belloni { 86ec187ef0SAlexandre Belloni struct clk_slow_osc *osc = to_clk_slow_osc(hw); 87ec187ef0SAlexandre Belloni void __iomem *sckcr = osc->sckcr; 88ec187ef0SAlexandre Belloni u32 tmp = readl(sckcr); 89ec187ef0SAlexandre Belloni 90abaceffcSClaudiu Beznea if (tmp & osc->bits->cr_osc32byp) 91ec187ef0SAlexandre Belloni return; 92ec187ef0SAlexandre Belloni 93abaceffcSClaudiu Beznea writel(tmp & ~osc->bits->cr_osc32en, sckcr); 94ec187ef0SAlexandre Belloni } 95ec187ef0SAlexandre Belloni 96ec187ef0SAlexandre Belloni static int clk_slow_osc_is_prepared(struct clk_hw *hw) 97ec187ef0SAlexandre Belloni { 98ec187ef0SAlexandre Belloni struct clk_slow_osc *osc = to_clk_slow_osc(hw); 99ec187ef0SAlexandre Belloni void __iomem *sckcr = osc->sckcr; 100ec187ef0SAlexandre Belloni u32 tmp = readl(sckcr); 101ec187ef0SAlexandre Belloni 102abaceffcSClaudiu Beznea if (tmp & osc->bits->cr_osc32byp) 103ec187ef0SAlexandre Belloni return 1; 104ec187ef0SAlexandre Belloni 105abaceffcSClaudiu Beznea return !!(tmp & osc->bits->cr_osc32en); 106ec187ef0SAlexandre Belloni } 107ec187ef0SAlexandre Belloni 108ec187ef0SAlexandre Belloni static const struct clk_ops slow_osc_ops = { 109ec187ef0SAlexandre Belloni .prepare = clk_slow_osc_prepare, 110ec187ef0SAlexandre Belloni .unprepare = clk_slow_osc_unprepare, 111ec187ef0SAlexandre Belloni .is_prepared = clk_slow_osc_is_prepared, 112ec187ef0SAlexandre Belloni }; 113ec187ef0SAlexandre Belloni 114ec187ef0SAlexandre Belloni static struct clk_hw * __init 115ec187ef0SAlexandre Belloni at91_clk_register_slow_osc(void __iomem *sckcr, 116ec187ef0SAlexandre Belloni const char *name, 117ec187ef0SAlexandre Belloni const char *parent_name, 118ec187ef0SAlexandre Belloni unsigned long startup, 119abaceffcSClaudiu Beznea bool bypass, 120abaceffcSClaudiu Beznea const struct clk_slow_bits *bits) 121ec187ef0SAlexandre Belloni { 122ec187ef0SAlexandre Belloni struct clk_slow_osc *osc; 123ec187ef0SAlexandre Belloni struct clk_hw *hw; 124ec187ef0SAlexandre Belloni struct clk_init_data init; 125ec187ef0SAlexandre Belloni int ret; 126ec187ef0SAlexandre Belloni 127ec187ef0SAlexandre Belloni if (!sckcr || !name || !parent_name) 128ec187ef0SAlexandre Belloni return ERR_PTR(-EINVAL); 129ec187ef0SAlexandre Belloni 130ec187ef0SAlexandre Belloni osc = kzalloc(sizeof(*osc), GFP_KERNEL); 131ec187ef0SAlexandre Belloni if (!osc) 132ec187ef0SAlexandre Belloni return ERR_PTR(-ENOMEM); 133ec187ef0SAlexandre Belloni 134ec187ef0SAlexandre Belloni init.name = name; 135ec187ef0SAlexandre Belloni init.ops = &slow_osc_ops; 136ec187ef0SAlexandre Belloni init.parent_names = &parent_name; 137ec187ef0SAlexandre Belloni init.num_parents = 1; 138ec187ef0SAlexandre Belloni init.flags = CLK_IGNORE_UNUSED; 139ec187ef0SAlexandre Belloni 140ec187ef0SAlexandre Belloni osc->hw.init = &init; 141ec187ef0SAlexandre Belloni osc->sckcr = sckcr; 142ec187ef0SAlexandre Belloni osc->startup_usec = startup; 143abaceffcSClaudiu Beznea osc->bits = bits; 144ec187ef0SAlexandre Belloni 145ec187ef0SAlexandre Belloni if (bypass) 146abaceffcSClaudiu Beznea writel((readl(sckcr) & ~osc->bits->cr_osc32en) | 147abaceffcSClaudiu Beznea osc->bits->cr_osc32byp, sckcr); 148ec187ef0SAlexandre Belloni 149ec187ef0SAlexandre Belloni hw = &osc->hw; 150ec187ef0SAlexandre Belloni ret = clk_hw_register(NULL, &osc->hw); 151ec187ef0SAlexandre Belloni if (ret) { 152ec187ef0SAlexandre Belloni kfree(osc); 153ec187ef0SAlexandre Belloni hw = ERR_PTR(ret); 154ec187ef0SAlexandre Belloni } 155ec187ef0SAlexandre Belloni 156ec187ef0SAlexandre Belloni return hw; 157ec187ef0SAlexandre Belloni } 158ec187ef0SAlexandre Belloni 1597fb791d0SClaudiu Beznea static void at91_clk_unregister_slow_osc(struct clk_hw *hw) 1607fb791d0SClaudiu Beznea { 1617fb791d0SClaudiu Beznea struct clk_slow_osc *osc = to_clk_slow_osc(hw); 1627fb791d0SClaudiu Beznea 1637fb791d0SClaudiu Beznea clk_hw_unregister(hw); 1647fb791d0SClaudiu Beznea kfree(osc); 1657fb791d0SClaudiu Beznea } 1667fb791d0SClaudiu Beznea 167ec187ef0SAlexandre Belloni static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw, 168ec187ef0SAlexandre Belloni unsigned long parent_rate) 169ec187ef0SAlexandre Belloni { 170ec187ef0SAlexandre Belloni struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 171ec187ef0SAlexandre Belloni 172ec187ef0SAlexandre Belloni return osc->frequency; 173ec187ef0SAlexandre Belloni } 174ec187ef0SAlexandre Belloni 175ec187ef0SAlexandre Belloni static unsigned long clk_slow_rc_osc_recalc_accuracy(struct clk_hw *hw, 176ec187ef0SAlexandre Belloni unsigned long parent_acc) 177ec187ef0SAlexandre Belloni { 178ec187ef0SAlexandre Belloni struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 179ec187ef0SAlexandre Belloni 180ec187ef0SAlexandre Belloni return osc->accuracy; 181ec187ef0SAlexandre Belloni } 182ec187ef0SAlexandre Belloni 183ec187ef0SAlexandre Belloni static int clk_slow_rc_osc_prepare(struct clk_hw *hw) 184ec187ef0SAlexandre Belloni { 185ec187ef0SAlexandre Belloni struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 186ec187ef0SAlexandre Belloni void __iomem *sckcr = osc->sckcr; 187ec187ef0SAlexandre Belloni 188abaceffcSClaudiu Beznea writel(readl(sckcr) | osc->bits->cr_rcen, sckcr); 189ec187ef0SAlexandre Belloni 190ec187ef0SAlexandre Belloni usleep_range(osc->startup_usec, osc->startup_usec + 1); 191ec187ef0SAlexandre Belloni 192ec187ef0SAlexandre Belloni return 0; 193ec187ef0SAlexandre Belloni } 194ec187ef0SAlexandre Belloni 195ec187ef0SAlexandre Belloni static void clk_slow_rc_osc_unprepare(struct clk_hw *hw) 196ec187ef0SAlexandre Belloni { 197ec187ef0SAlexandre Belloni struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 198ec187ef0SAlexandre Belloni void __iomem *sckcr = osc->sckcr; 199ec187ef0SAlexandre Belloni 200abaceffcSClaudiu Beznea writel(readl(sckcr) & ~osc->bits->cr_rcen, sckcr); 201ec187ef0SAlexandre Belloni } 202ec187ef0SAlexandre Belloni 203ec187ef0SAlexandre Belloni static int clk_slow_rc_osc_is_prepared(struct clk_hw *hw) 204ec187ef0SAlexandre Belloni { 205ec187ef0SAlexandre Belloni struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 206ec187ef0SAlexandre Belloni 207abaceffcSClaudiu Beznea return !!(readl(osc->sckcr) & osc->bits->cr_rcen); 208ec187ef0SAlexandre Belloni } 209ec187ef0SAlexandre Belloni 210ec187ef0SAlexandre Belloni static const struct clk_ops slow_rc_osc_ops = { 211ec187ef0SAlexandre Belloni .prepare = clk_slow_rc_osc_prepare, 212ec187ef0SAlexandre Belloni .unprepare = clk_slow_rc_osc_unprepare, 213ec187ef0SAlexandre Belloni .is_prepared = clk_slow_rc_osc_is_prepared, 214ec187ef0SAlexandre Belloni .recalc_rate = clk_slow_rc_osc_recalc_rate, 215ec187ef0SAlexandre Belloni .recalc_accuracy = clk_slow_rc_osc_recalc_accuracy, 216ec187ef0SAlexandre Belloni }; 217ec187ef0SAlexandre Belloni 218ec187ef0SAlexandre Belloni static struct clk_hw * __init 219ec187ef0SAlexandre Belloni at91_clk_register_slow_rc_osc(void __iomem *sckcr, 220ec187ef0SAlexandre Belloni const char *name, 221ec187ef0SAlexandre Belloni unsigned long frequency, 222ec187ef0SAlexandre Belloni unsigned long accuracy, 223abaceffcSClaudiu Beznea unsigned long startup, 224abaceffcSClaudiu Beznea const struct clk_slow_bits *bits) 225ec187ef0SAlexandre Belloni { 226ec187ef0SAlexandre Belloni struct clk_slow_rc_osc *osc; 227ec187ef0SAlexandre Belloni struct clk_hw *hw; 228ec187ef0SAlexandre Belloni struct clk_init_data init; 229ec187ef0SAlexandre Belloni int ret; 230ec187ef0SAlexandre Belloni 231ec187ef0SAlexandre Belloni if (!sckcr || !name) 232ec187ef0SAlexandre Belloni return ERR_PTR(-EINVAL); 233ec187ef0SAlexandre Belloni 234ec187ef0SAlexandre Belloni osc = kzalloc(sizeof(*osc), GFP_KERNEL); 235ec187ef0SAlexandre Belloni if (!osc) 236ec187ef0SAlexandre Belloni return ERR_PTR(-ENOMEM); 237ec187ef0SAlexandre Belloni 238ec187ef0SAlexandre Belloni init.name = name; 239ec187ef0SAlexandre Belloni init.ops = &slow_rc_osc_ops; 240ec187ef0SAlexandre Belloni init.parent_names = NULL; 241ec187ef0SAlexandre Belloni init.num_parents = 0; 242ec187ef0SAlexandre Belloni init.flags = CLK_IGNORE_UNUSED; 243ec187ef0SAlexandre Belloni 244ec187ef0SAlexandre Belloni osc->hw.init = &init; 245ec187ef0SAlexandre Belloni osc->sckcr = sckcr; 246abaceffcSClaudiu Beznea osc->bits = bits; 247ec187ef0SAlexandre Belloni osc->frequency = frequency; 248ec187ef0SAlexandre Belloni osc->accuracy = accuracy; 249ec187ef0SAlexandre Belloni osc->startup_usec = startup; 250ec187ef0SAlexandre Belloni 251ec187ef0SAlexandre Belloni hw = &osc->hw; 252ec187ef0SAlexandre Belloni ret = clk_hw_register(NULL, &osc->hw); 253ec187ef0SAlexandre Belloni if (ret) { 254ec187ef0SAlexandre Belloni kfree(osc); 255ec187ef0SAlexandre Belloni hw = ERR_PTR(ret); 256ec187ef0SAlexandre Belloni } 257ec187ef0SAlexandre Belloni 258ec187ef0SAlexandre Belloni return hw; 259ec187ef0SAlexandre Belloni } 260ec187ef0SAlexandre Belloni 26103670246SClaudiu Beznea static void at91_clk_unregister_slow_rc_osc(struct clk_hw *hw) 26203670246SClaudiu Beznea { 26303670246SClaudiu Beznea struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 26403670246SClaudiu Beznea 26503670246SClaudiu Beznea clk_hw_unregister(hw); 26603670246SClaudiu Beznea kfree(osc); 26703670246SClaudiu Beznea } 26803670246SClaudiu Beznea 269ec187ef0SAlexandre Belloni static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index) 270ec187ef0SAlexandre Belloni { 271ec187ef0SAlexandre Belloni struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); 272ec187ef0SAlexandre Belloni void __iomem *sckcr = slowck->sckcr; 273ec187ef0SAlexandre Belloni u32 tmp; 274ec187ef0SAlexandre Belloni 275ec187ef0SAlexandre Belloni if (index > 1) 276ec187ef0SAlexandre Belloni return -EINVAL; 277ec187ef0SAlexandre Belloni 278ec187ef0SAlexandre Belloni tmp = readl(sckcr); 279ec187ef0SAlexandre Belloni 280abaceffcSClaudiu Beznea if ((!index && !(tmp & slowck->bits->cr_oscsel)) || 281abaceffcSClaudiu Beznea (index && (tmp & slowck->bits->cr_oscsel))) 282ec187ef0SAlexandre Belloni return 0; 283ec187ef0SAlexandre Belloni 284ec187ef0SAlexandre Belloni if (index) 285abaceffcSClaudiu Beznea tmp |= slowck->bits->cr_oscsel; 286ec187ef0SAlexandre Belloni else 287abaceffcSClaudiu Beznea tmp &= ~slowck->bits->cr_oscsel; 288ec187ef0SAlexandre Belloni 289ec187ef0SAlexandre Belloni writel(tmp, sckcr); 290ec187ef0SAlexandre Belloni 291ec187ef0SAlexandre Belloni usleep_range(SLOWCK_SW_TIME_USEC, SLOWCK_SW_TIME_USEC + 1); 292ec187ef0SAlexandre Belloni 293ec187ef0SAlexandre Belloni return 0; 294ec187ef0SAlexandre Belloni } 295ec187ef0SAlexandre Belloni 296ec187ef0SAlexandre Belloni static u8 clk_sam9x5_slow_get_parent(struct clk_hw *hw) 297ec187ef0SAlexandre Belloni { 298ec187ef0SAlexandre Belloni struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); 299ec187ef0SAlexandre Belloni 300abaceffcSClaudiu Beznea return !!(readl(slowck->sckcr) & slowck->bits->cr_oscsel); 301ec187ef0SAlexandre Belloni } 302ec187ef0SAlexandre Belloni 303ec187ef0SAlexandre Belloni static const struct clk_ops sam9x5_slow_ops = { 304ec187ef0SAlexandre Belloni .set_parent = clk_sam9x5_slow_set_parent, 305ec187ef0SAlexandre Belloni .get_parent = clk_sam9x5_slow_get_parent, 306ec187ef0SAlexandre Belloni }; 307ec187ef0SAlexandre Belloni 308ec187ef0SAlexandre Belloni static struct clk_hw * __init 309ec187ef0SAlexandre Belloni at91_clk_register_sam9x5_slow(void __iomem *sckcr, 310ec187ef0SAlexandre Belloni const char *name, 311ec187ef0SAlexandre Belloni const char **parent_names, 312abaceffcSClaudiu Beznea int num_parents, 313abaceffcSClaudiu Beznea const struct clk_slow_bits *bits) 314ec187ef0SAlexandre Belloni { 315ec187ef0SAlexandre Belloni struct clk_sam9x5_slow *slowck; 316ec187ef0SAlexandre Belloni struct clk_hw *hw; 317ec187ef0SAlexandre Belloni struct clk_init_data init; 318ec187ef0SAlexandre Belloni int ret; 319ec187ef0SAlexandre Belloni 320ec187ef0SAlexandre Belloni if (!sckcr || !name || !parent_names || !num_parents) 321ec187ef0SAlexandre Belloni return ERR_PTR(-EINVAL); 322ec187ef0SAlexandre Belloni 323ec187ef0SAlexandre Belloni slowck = kzalloc(sizeof(*slowck), GFP_KERNEL); 324ec187ef0SAlexandre Belloni if (!slowck) 325ec187ef0SAlexandre Belloni return ERR_PTR(-ENOMEM); 326ec187ef0SAlexandre Belloni 327ec187ef0SAlexandre Belloni init.name = name; 328ec187ef0SAlexandre Belloni init.ops = &sam9x5_slow_ops; 329ec187ef0SAlexandre Belloni init.parent_names = parent_names; 330ec187ef0SAlexandre Belloni init.num_parents = num_parents; 331ec187ef0SAlexandre Belloni init.flags = 0; 332ec187ef0SAlexandre Belloni 333ec187ef0SAlexandre Belloni slowck->hw.init = &init; 334ec187ef0SAlexandre Belloni slowck->sckcr = sckcr; 335abaceffcSClaudiu Beznea slowck->bits = bits; 336abaceffcSClaudiu Beznea slowck->parent = !!(readl(sckcr) & slowck->bits->cr_oscsel); 337ec187ef0SAlexandre Belloni 338ec187ef0SAlexandre Belloni hw = &slowck->hw; 339ec187ef0SAlexandre Belloni ret = clk_hw_register(NULL, &slowck->hw); 340ec187ef0SAlexandre Belloni if (ret) { 341ec187ef0SAlexandre Belloni kfree(slowck); 342ec187ef0SAlexandre Belloni hw = ERR_PTR(ret); 343ec187ef0SAlexandre Belloni } 344ec187ef0SAlexandre Belloni 345ec187ef0SAlexandre Belloni return hw; 346ec187ef0SAlexandre Belloni } 347ec187ef0SAlexandre Belloni 348016d22ddSClaudiu Beznea static void at91_clk_unregister_sam9x5_slow(struct clk_hw *hw) 349016d22ddSClaudiu Beznea { 350016d22ddSClaudiu Beznea struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); 351016d22ddSClaudiu Beznea 352016d22ddSClaudiu Beznea clk_hw_unregister(hw); 353016d22ddSClaudiu Beznea kfree(slowck); 354016d22ddSClaudiu Beznea } 355016d22ddSClaudiu Beznea 3565c16ffa7SAlexandre Belloni static void __init at91sam9x5_sckc_register(struct device_node *np, 357abaceffcSClaudiu Beznea unsigned int rc_osc_startup_us, 358abaceffcSClaudiu Beznea const struct clk_slow_bits *bits) 35980eded6cSBoris BREZILLON { 36045b5ec84SAlexandre Belloni const char *parent_names[2] = { "slow_rc_osc", "slow_osc" }; 36180eded6cSBoris BREZILLON void __iomem *regbase = of_iomap(np, 0); 36245b5ec84SAlexandre Belloni struct device_node *child = NULL; 36345b5ec84SAlexandre Belloni const char *xtal_name; 36482e25dc8SClaudiu Beznea struct clk_hw *slow_rc, *slow_osc, *slowck; 36545b5ec84SAlexandre Belloni bool bypass; 36682e25dc8SClaudiu Beznea int ret; 36780eded6cSBoris BREZILLON 36880eded6cSBoris BREZILLON if (!regbase) 36980eded6cSBoris BREZILLON return; 37080eded6cSBoris BREZILLON 37182e25dc8SClaudiu Beznea slow_rc = at91_clk_register_slow_rc_osc(regbase, parent_names[0], 37282e25dc8SClaudiu Beznea 32768, 50000000, 37382e25dc8SClaudiu Beznea rc_osc_startup_us, bits); 37482e25dc8SClaudiu Beznea if (IS_ERR(slow_rc)) 37545b5ec84SAlexandre Belloni return; 37645b5ec84SAlexandre Belloni 37745b5ec84SAlexandre Belloni xtal_name = of_clk_get_parent_name(np, 0); 37845b5ec84SAlexandre Belloni if (!xtal_name) { 37945b5ec84SAlexandre Belloni /* DT backward compatibility */ 38045b5ec84SAlexandre Belloni child = of_get_compatible_child(np, "atmel,at91sam9x5-clk-slow-osc"); 38145b5ec84SAlexandre Belloni if (!child) 38282e25dc8SClaudiu Beznea goto unregister_slow_rc; 38345b5ec84SAlexandre Belloni 38445b5ec84SAlexandre Belloni xtal_name = of_clk_get_parent_name(child, 0); 38545b5ec84SAlexandre Belloni bypass = of_property_read_bool(child, "atmel,osc-bypass"); 38645b5ec84SAlexandre Belloni 38745b5ec84SAlexandre Belloni child = of_get_compatible_child(np, "atmel,at91sam9x5-clk-slow"); 38845b5ec84SAlexandre Belloni } else { 38945b5ec84SAlexandre Belloni bypass = of_property_read_bool(np, "atmel,osc-bypass"); 39080eded6cSBoris BREZILLON } 39145b5ec84SAlexandre Belloni 39245b5ec84SAlexandre Belloni if (!xtal_name) 39382e25dc8SClaudiu Beznea goto unregister_slow_rc; 39445b5ec84SAlexandre Belloni 39582e25dc8SClaudiu Beznea slow_osc = at91_clk_register_slow_osc(regbase, parent_names[1], 39682e25dc8SClaudiu Beznea xtal_name, 1200000, bypass, bits); 39782e25dc8SClaudiu Beznea if (IS_ERR(slow_osc)) 39882e25dc8SClaudiu Beznea goto unregister_slow_rc; 39945b5ec84SAlexandre Belloni 40082e25dc8SClaudiu Beznea slowck = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_names, 40182e25dc8SClaudiu Beznea 2, bits); 40282e25dc8SClaudiu Beznea if (IS_ERR(slowck)) 40382e25dc8SClaudiu Beznea goto unregister_slow_osc; 40445b5ec84SAlexandre Belloni 40545b5ec84SAlexandre Belloni /* DT backward compatibility */ 40645b5ec84SAlexandre Belloni if (child) 40782e25dc8SClaudiu Beznea ret = of_clk_add_hw_provider(child, of_clk_hw_simple_get, 40882e25dc8SClaudiu Beznea slowck); 40982e25dc8SClaudiu Beznea else 41082e25dc8SClaudiu Beznea ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, slowck); 41182e25dc8SClaudiu Beznea 41282e25dc8SClaudiu Beznea if (WARN_ON(ret)) 41382e25dc8SClaudiu Beznea goto unregister_slowck; 41482e25dc8SClaudiu Beznea 41582e25dc8SClaudiu Beznea return; 41682e25dc8SClaudiu Beznea 41782e25dc8SClaudiu Beznea unregister_slowck: 41882e25dc8SClaudiu Beznea at91_clk_unregister_sam9x5_slow(slowck); 41982e25dc8SClaudiu Beznea unregister_slow_osc: 42082e25dc8SClaudiu Beznea at91_clk_unregister_slow_osc(slow_osc); 42182e25dc8SClaudiu Beznea unregister_slow_rc: 42282e25dc8SClaudiu Beznea at91_clk_unregister_slow_rc_osc(slow_rc); 42380eded6cSBoris BREZILLON } 4245c16ffa7SAlexandre Belloni 425abaceffcSClaudiu Beznea static const struct clk_slow_bits at91sam9x5_bits = { 426abaceffcSClaudiu Beznea .cr_rcen = BIT(0), 427abaceffcSClaudiu Beznea .cr_osc32en = BIT(1), 428abaceffcSClaudiu Beznea .cr_osc32byp = BIT(2), 429abaceffcSClaudiu Beznea .cr_oscsel = BIT(3), 430abaceffcSClaudiu Beznea }; 431abaceffcSClaudiu Beznea 4325c16ffa7SAlexandre Belloni static void __init of_at91sam9x5_sckc_setup(struct device_node *np) 4335c16ffa7SAlexandre Belloni { 434abaceffcSClaudiu Beznea at91sam9x5_sckc_register(np, 75, &at91sam9x5_bits); 4355c16ffa7SAlexandre Belloni } 43680eded6cSBoris BREZILLON CLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc", 43780eded6cSBoris BREZILLON of_at91sam9x5_sckc_setup); 4384b13b645SAlexandre Belloni 4395c16ffa7SAlexandre Belloni static void __init of_sama5d3_sckc_setup(struct device_node *np) 4405c16ffa7SAlexandre Belloni { 441abaceffcSClaudiu Beznea at91sam9x5_sckc_register(np, 500, &at91sam9x5_bits); 4425c16ffa7SAlexandre Belloni } 4435c16ffa7SAlexandre Belloni CLK_OF_DECLARE(sama5d3_clk_sckc, "atmel,sama5d3-sckc", 4445c16ffa7SAlexandre Belloni of_sama5d3_sckc_setup); 4455c16ffa7SAlexandre Belloni 44604bcc427SClaudiu Beznea static const struct clk_slow_bits at91sam9x60_bits = { 44704bcc427SClaudiu Beznea .cr_osc32en = BIT(1), 44804bcc427SClaudiu Beznea .cr_osc32byp = BIT(2), 44904bcc427SClaudiu Beznea .cr_oscsel = BIT(24), 45004bcc427SClaudiu Beznea }; 45104bcc427SClaudiu Beznea 45204bcc427SClaudiu Beznea static void __init of_sam9x60_sckc_setup(struct device_node *np) 45304bcc427SClaudiu Beznea { 45404bcc427SClaudiu Beznea void __iomem *regbase = of_iomap(np, 0); 45504bcc427SClaudiu Beznea struct clk_hw_onecell_data *clk_data; 45604bcc427SClaudiu Beznea struct clk_hw *slow_rc, *slow_osc; 45704bcc427SClaudiu Beznea const char *xtal_name; 45804bcc427SClaudiu Beznea const char *parent_names[2] = { "slow_rc_osc", "slow_osc" }; 45904bcc427SClaudiu Beznea bool bypass; 46004bcc427SClaudiu Beznea int ret; 46104bcc427SClaudiu Beznea 46204bcc427SClaudiu Beznea if (!regbase) 46304bcc427SClaudiu Beznea return; 46404bcc427SClaudiu Beznea 46504bcc427SClaudiu Beznea slow_rc = clk_hw_register_fixed_rate(NULL, parent_names[0], NULL, 0, 46604bcc427SClaudiu Beznea 32768); 46704bcc427SClaudiu Beznea if (IS_ERR(slow_rc)) 46804bcc427SClaudiu Beznea return; 46904bcc427SClaudiu Beznea 47004bcc427SClaudiu Beznea xtal_name = of_clk_get_parent_name(np, 0); 47104bcc427SClaudiu Beznea if (!xtal_name) 47204bcc427SClaudiu Beznea goto unregister_slow_rc; 47304bcc427SClaudiu Beznea 47404bcc427SClaudiu Beznea bypass = of_property_read_bool(np, "atmel,osc-bypass"); 47504bcc427SClaudiu Beznea slow_osc = at91_clk_register_slow_osc(regbase, parent_names[1], 47604bcc427SClaudiu Beznea xtal_name, 5000000, bypass, 47704bcc427SClaudiu Beznea &at91sam9x60_bits); 47804bcc427SClaudiu Beznea if (IS_ERR(slow_osc)) 47904bcc427SClaudiu Beznea goto unregister_slow_rc; 48004bcc427SClaudiu Beznea 481e620a1e0SStephen Kitt clk_data = kzalloc(struct_size(clk_data, hws, 2), GFP_KERNEL); 48204bcc427SClaudiu Beznea if (!clk_data) 48304bcc427SClaudiu Beznea goto unregister_slow_osc; 48404bcc427SClaudiu Beznea 48504bcc427SClaudiu Beznea /* MD_SLCK and TD_SLCK. */ 48604bcc427SClaudiu Beznea clk_data->num = 2; 48704bcc427SClaudiu Beznea clk_data->hws[0] = clk_hw_register_fixed_rate(NULL, "md_slck", 48804bcc427SClaudiu Beznea parent_names[0], 48904bcc427SClaudiu Beznea 0, 32768); 49004bcc427SClaudiu Beznea if (IS_ERR(clk_data->hws[0])) 49104bcc427SClaudiu Beznea goto clk_data_free; 49204bcc427SClaudiu Beznea 49304bcc427SClaudiu Beznea clk_data->hws[1] = at91_clk_register_sam9x5_slow(regbase, "td_slck", 49404bcc427SClaudiu Beznea parent_names, 2, 49504bcc427SClaudiu Beznea &at91sam9x60_bits); 49604bcc427SClaudiu Beznea if (IS_ERR(clk_data->hws[1])) 49704bcc427SClaudiu Beznea goto unregister_md_slck; 49804bcc427SClaudiu Beznea 49904bcc427SClaudiu Beznea ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data); 50004bcc427SClaudiu Beznea if (WARN_ON(ret)) 50104bcc427SClaudiu Beznea goto unregister_td_slck; 50204bcc427SClaudiu Beznea 50304bcc427SClaudiu Beznea return; 50404bcc427SClaudiu Beznea 50504bcc427SClaudiu Beznea unregister_td_slck: 506ecbcc2aaSClaudiu Beznea at91_clk_unregister_sam9x5_slow(clk_data->hws[1]); 50704bcc427SClaudiu Beznea unregister_md_slck: 50804bcc427SClaudiu Beznea clk_hw_unregister(clk_data->hws[0]); 50904bcc427SClaudiu Beznea clk_data_free: 51004bcc427SClaudiu Beznea kfree(clk_data); 51104bcc427SClaudiu Beznea unregister_slow_osc: 512ecbcc2aaSClaudiu Beznea at91_clk_unregister_slow_osc(slow_osc); 51304bcc427SClaudiu Beznea unregister_slow_rc: 51404bcc427SClaudiu Beznea clk_hw_unregister(slow_rc); 51504bcc427SClaudiu Beznea } 51604bcc427SClaudiu Beznea CLK_OF_DECLARE(sam9x60_clk_sckc, "microchip,sam9x60-sckc", 51704bcc427SClaudiu Beznea of_sam9x60_sckc_setup); 51804bcc427SClaudiu Beznea 5194b13b645SAlexandre Belloni static int clk_sama5d4_slow_osc_prepare(struct clk_hw *hw) 5204b13b645SAlexandre Belloni { 5214b13b645SAlexandre Belloni struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw); 5224b13b645SAlexandre Belloni 5234b13b645SAlexandre Belloni if (osc->prepared) 5244b13b645SAlexandre Belloni return 0; 5254b13b645SAlexandre Belloni 5264b13b645SAlexandre Belloni /* 5274b13b645SAlexandre Belloni * Assume that if it has already been selected (for example by the 5284b13b645SAlexandre Belloni * bootloader), enough time has aready passed. 5294b13b645SAlexandre Belloni */ 530abaceffcSClaudiu Beznea if ((readl(osc->sckcr) & osc->bits->cr_oscsel)) { 5314b13b645SAlexandre Belloni osc->prepared = true; 5324b13b645SAlexandre Belloni return 0; 5334b13b645SAlexandre Belloni } 5344b13b645SAlexandre Belloni 5354b13b645SAlexandre Belloni usleep_range(osc->startup_usec, osc->startup_usec + 1); 5364b13b645SAlexandre Belloni osc->prepared = true; 5374b13b645SAlexandre Belloni 5384b13b645SAlexandre Belloni return 0; 5394b13b645SAlexandre Belloni } 5404b13b645SAlexandre Belloni 5414b13b645SAlexandre Belloni static int clk_sama5d4_slow_osc_is_prepared(struct clk_hw *hw) 5424b13b645SAlexandre Belloni { 5434b13b645SAlexandre Belloni struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw); 5444b13b645SAlexandre Belloni 5454b13b645SAlexandre Belloni return osc->prepared; 5464b13b645SAlexandre Belloni } 5474b13b645SAlexandre Belloni 5484b13b645SAlexandre Belloni static const struct clk_ops sama5d4_slow_osc_ops = { 5494b13b645SAlexandre Belloni .prepare = clk_sama5d4_slow_osc_prepare, 5504b13b645SAlexandre Belloni .is_prepared = clk_sama5d4_slow_osc_is_prepared, 5514b13b645SAlexandre Belloni }; 5524b13b645SAlexandre Belloni 553abaceffcSClaudiu Beznea static const struct clk_slow_bits at91sama5d4_bits = { 554abaceffcSClaudiu Beznea .cr_oscsel = BIT(3), 555abaceffcSClaudiu Beznea }; 556abaceffcSClaudiu Beznea 5574b13b645SAlexandre Belloni static void __init of_sama5d4_sckc_setup(struct device_node *np) 5584b13b645SAlexandre Belloni { 5594b13b645SAlexandre Belloni void __iomem *regbase = of_iomap(np, 0); 560d09e6ca1SClaudiu Beznea struct clk_hw *slow_rc, *slowck; 5614b13b645SAlexandre Belloni struct clk_sama5d4_slow_osc *osc; 5624b13b645SAlexandre Belloni struct clk_init_data init; 5634b13b645SAlexandre Belloni const char *xtal_name; 5644b13b645SAlexandre Belloni const char *parent_names[2] = { "slow_rc_osc", "slow_osc" }; 5654b13b645SAlexandre Belloni int ret; 5664b13b645SAlexandre Belloni 5674b13b645SAlexandre Belloni if (!regbase) 5684b13b645SAlexandre Belloni return; 5694b13b645SAlexandre Belloni 570d09e6ca1SClaudiu Beznea slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL, 571d09e6ca1SClaudiu Beznea parent_names[0], 5724b13b645SAlexandre Belloni NULL, 0, 32768, 5734b13b645SAlexandre Belloni 250000000); 574d09e6ca1SClaudiu Beznea if (IS_ERR(slow_rc)) 5754b13b645SAlexandre Belloni return; 5764b13b645SAlexandre Belloni 5774b13b645SAlexandre Belloni xtal_name = of_clk_get_parent_name(np, 0); 5784b13b645SAlexandre Belloni 5794b13b645SAlexandre Belloni osc = kzalloc(sizeof(*osc), GFP_KERNEL); 5804b13b645SAlexandre Belloni if (!osc) 581d09e6ca1SClaudiu Beznea goto unregister_slow_rc; 5824b13b645SAlexandre Belloni 5834b13b645SAlexandre Belloni init.name = parent_names[1]; 5844b13b645SAlexandre Belloni init.ops = &sama5d4_slow_osc_ops; 5854b13b645SAlexandre Belloni init.parent_names = &xtal_name; 5864b13b645SAlexandre Belloni init.num_parents = 1; 5874b13b645SAlexandre Belloni init.flags = CLK_IGNORE_UNUSED; 5884b13b645SAlexandre Belloni 5894b13b645SAlexandre Belloni osc->hw.init = &init; 5904b13b645SAlexandre Belloni osc->sckcr = regbase; 5914b13b645SAlexandre Belloni osc->startup_usec = 1200000; 592abaceffcSClaudiu Beznea osc->bits = &at91sama5d4_bits; 5934b13b645SAlexandre Belloni 5944b13b645SAlexandre Belloni ret = clk_hw_register(NULL, &osc->hw); 595d09e6ca1SClaudiu Beznea if (ret) 596d09e6ca1SClaudiu Beznea goto free_slow_osc_data; 5974b13b645SAlexandre Belloni 598d09e6ca1SClaudiu Beznea slowck = at91_clk_register_sam9x5_slow(regbase, "slowck", 599d09e6ca1SClaudiu Beznea parent_names, 2, 600abaceffcSClaudiu Beznea &at91sama5d4_bits); 601d09e6ca1SClaudiu Beznea if (IS_ERR(slowck)) 602d09e6ca1SClaudiu Beznea goto unregister_slow_osc; 603d09e6ca1SClaudiu Beznea 604d09e6ca1SClaudiu Beznea ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, slowck); 605d09e6ca1SClaudiu Beznea if (WARN_ON(ret)) 606d09e6ca1SClaudiu Beznea goto unregister_slowck; 607d09e6ca1SClaudiu Beznea 6084b13b645SAlexandre Belloni return; 6094b13b645SAlexandre Belloni 610d09e6ca1SClaudiu Beznea unregister_slowck: 611d09e6ca1SClaudiu Beznea at91_clk_unregister_sam9x5_slow(slowck); 612d09e6ca1SClaudiu Beznea unregister_slow_osc: 613d09e6ca1SClaudiu Beznea clk_hw_unregister(&osc->hw); 614d09e6ca1SClaudiu Beznea free_slow_osc_data: 6154b13b645SAlexandre Belloni kfree(osc); 616d09e6ca1SClaudiu Beznea unregister_slow_rc: 617d09e6ca1SClaudiu Beznea clk_hw_unregister(slow_rc); 6184b13b645SAlexandre Belloni } 6194b13b645SAlexandre Belloni CLK_OF_DECLARE(sama5d4_clk_sckc, "atmel,sama5d4-sckc", 6204b13b645SAlexandre Belloni of_sama5d4_sckc_setup); 621