656 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * drivers/clk/at91/sckc.c4 *5 * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>6 */7 8#include <linux/clk-provider.h>9#include <linux/clkdev.h>10#include <linux/delay.h>11#include <linux/of.h>12#include <linux/of_address.h>13#include <linux/io.h>14 15#define SLOW_CLOCK_FREQ 3276816#define SLOWCK_SW_CYCLES 517#define SLOWCK_SW_TIME_USEC ((SLOWCK_SW_CYCLES * USEC_PER_SEC) / \18 SLOW_CLOCK_FREQ)19 20#define AT91_SCKC_CR 0x0021 22struct clk_slow_bits {23 u32 cr_rcen;24 u32 cr_osc32en;25 u32 cr_osc32byp;26 u32 cr_oscsel;27};28 29struct clk_slow_osc {30 struct clk_hw hw;31 void __iomem *sckcr;32 const struct clk_slow_bits *bits;33 unsigned long startup_usec;34};35 36#define to_clk_slow_osc(hw) container_of(hw, struct clk_slow_osc, hw)37 38struct clk_sama5d4_slow_osc {39 struct clk_hw hw;40 void __iomem *sckcr;41 const struct clk_slow_bits *bits;42 unsigned long startup_usec;43 bool prepared;44};45 46#define to_clk_sama5d4_slow_osc(hw) container_of(hw, struct clk_sama5d4_slow_osc, hw)47 48struct clk_slow_rc_osc {49 struct clk_hw hw;50 void __iomem *sckcr;51 const struct clk_slow_bits *bits;52 unsigned long frequency;53 unsigned long accuracy;54 unsigned long startup_usec;55};56 57#define to_clk_slow_rc_osc(hw) container_of(hw, struct clk_slow_rc_osc, hw)58 59struct clk_sam9x5_slow {60 struct clk_hw hw;61 void __iomem *sckcr;62 const struct clk_slow_bits *bits;63 u8 parent;64};65 66#define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, hw)67 68static int clk_slow_osc_prepare(struct clk_hw *hw)69{70 struct clk_slow_osc *osc = to_clk_slow_osc(hw);71 void __iomem *sckcr = osc->sckcr;72 u32 tmp = readl(sckcr);73 74 if (tmp & (osc->bits->cr_osc32byp | osc->bits->cr_osc32en))75 return 0;76 77 writel(tmp | osc->bits->cr_osc32en, sckcr);78 79 if (system_state < SYSTEM_RUNNING)80 udelay(osc->startup_usec);81 else82 usleep_range(osc->startup_usec, osc->startup_usec + 1);83 84 return 0;85}86 87static void clk_slow_osc_unprepare(struct clk_hw *hw)88{89 struct clk_slow_osc *osc = to_clk_slow_osc(hw);90 void __iomem *sckcr = osc->sckcr;91 u32 tmp = readl(sckcr);92 93 if (tmp & osc->bits->cr_osc32byp)94 return;95 96 writel(tmp & ~osc->bits->cr_osc32en, sckcr);97}98 99static int clk_slow_osc_is_prepared(struct clk_hw *hw)100{101 struct clk_slow_osc *osc = to_clk_slow_osc(hw);102 void __iomem *sckcr = osc->sckcr;103 u32 tmp = readl(sckcr);104 105 if (tmp & osc->bits->cr_osc32byp)106 return 1;107 108 return !!(tmp & osc->bits->cr_osc32en);109}110 111static const struct clk_ops slow_osc_ops = {112 .prepare = clk_slow_osc_prepare,113 .unprepare = clk_slow_osc_unprepare,114 .is_prepared = clk_slow_osc_is_prepared,115};116 117static struct clk_hw * __init118at91_clk_register_slow_osc(void __iomem *sckcr,119 const char *name,120 const struct clk_parent_data *parent_data,121 unsigned long startup,122 bool bypass,123 const struct clk_slow_bits *bits)124{125 struct clk_slow_osc *osc;126 struct clk_hw *hw;127 struct clk_init_data init = {};128 int ret;129 130 if (!sckcr || !name || !parent_data)131 return ERR_PTR(-EINVAL);132 133 osc = kzalloc(sizeof(*osc), GFP_KERNEL);134 if (!osc)135 return ERR_PTR(-ENOMEM);136 137 init.name = name;138 init.ops = &slow_osc_ops;139 init.parent_data = parent_data;140 init.num_parents = 1;141 init.flags = CLK_IGNORE_UNUSED;142 143 osc->hw.init = &init;144 osc->sckcr = sckcr;145 osc->startup_usec = startup;146 osc->bits = bits;147 148 if (bypass)149 writel((readl(sckcr) & ~osc->bits->cr_osc32en) |150 osc->bits->cr_osc32byp, sckcr);151 152 hw = &osc->hw;153 ret = clk_hw_register(NULL, &osc->hw);154 if (ret) {155 kfree(osc);156 hw = ERR_PTR(ret);157 }158 159 return hw;160}161 162static void at91_clk_unregister_slow_osc(struct clk_hw *hw)163{164 struct clk_slow_osc *osc = to_clk_slow_osc(hw);165 166 clk_hw_unregister(hw);167 kfree(osc);168}169 170static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw,171 unsigned long parent_rate)172{173 struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);174 175 return osc->frequency;176}177 178static unsigned long clk_slow_rc_osc_recalc_accuracy(struct clk_hw *hw,179 unsigned long parent_acc)180{181 struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);182 183 return osc->accuracy;184}185 186static int clk_slow_rc_osc_prepare(struct clk_hw *hw)187{188 struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);189 void __iomem *sckcr = osc->sckcr;190 191 writel(readl(sckcr) | osc->bits->cr_rcen, sckcr);192 193 if (system_state < SYSTEM_RUNNING)194 udelay(osc->startup_usec);195 else196 usleep_range(osc->startup_usec, osc->startup_usec + 1);197 198 return 0;199}200 201static void clk_slow_rc_osc_unprepare(struct clk_hw *hw)202{203 struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);204 void __iomem *sckcr = osc->sckcr;205 206 writel(readl(sckcr) & ~osc->bits->cr_rcen, sckcr);207}208 209static int clk_slow_rc_osc_is_prepared(struct clk_hw *hw)210{211 struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);212 213 return !!(readl(osc->sckcr) & osc->bits->cr_rcen);214}215 216static const struct clk_ops slow_rc_osc_ops = {217 .prepare = clk_slow_rc_osc_prepare,218 .unprepare = clk_slow_rc_osc_unprepare,219 .is_prepared = clk_slow_rc_osc_is_prepared,220 .recalc_rate = clk_slow_rc_osc_recalc_rate,221 .recalc_accuracy = clk_slow_rc_osc_recalc_accuracy,222};223 224static struct clk_hw * __init225at91_clk_register_slow_rc_osc(void __iomem *sckcr,226 const char *name,227 unsigned long frequency,228 unsigned long accuracy,229 unsigned long startup,230 const struct clk_slow_bits *bits)231{232 struct clk_slow_rc_osc *osc;233 struct clk_hw *hw;234 struct clk_init_data init;235 int ret;236 237 if (!sckcr || !name)238 return ERR_PTR(-EINVAL);239 240 osc = kzalloc(sizeof(*osc), GFP_KERNEL);241 if (!osc)242 return ERR_PTR(-ENOMEM);243 244 init.name = name;245 init.ops = &slow_rc_osc_ops;246 init.parent_names = NULL;247 init.num_parents = 0;248 init.flags = CLK_IGNORE_UNUSED;249 250 osc->hw.init = &init;251 osc->sckcr = sckcr;252 osc->bits = bits;253 osc->frequency = frequency;254 osc->accuracy = accuracy;255 osc->startup_usec = startup;256 257 hw = &osc->hw;258 ret = clk_hw_register(NULL, &osc->hw);259 if (ret) {260 kfree(osc);261 hw = ERR_PTR(ret);262 }263 264 return hw;265}266 267static void at91_clk_unregister_slow_rc_osc(struct clk_hw *hw)268{269 struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);270 271 clk_hw_unregister(hw);272 kfree(osc);273}274 275static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index)276{277 struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);278 void __iomem *sckcr = slowck->sckcr;279 u32 tmp;280 281 if (index > 1)282 return -EINVAL;283 284 tmp = readl(sckcr);285 286 if ((!index && !(tmp & slowck->bits->cr_oscsel)) ||287 (index && (tmp & slowck->bits->cr_oscsel)))288 return 0;289 290 if (index)291 tmp |= slowck->bits->cr_oscsel;292 else293 tmp &= ~slowck->bits->cr_oscsel;294 295 writel(tmp, sckcr);296 297 if (system_state < SYSTEM_RUNNING)298 udelay(SLOWCK_SW_TIME_USEC);299 else300 usleep_range(SLOWCK_SW_TIME_USEC, SLOWCK_SW_TIME_USEC + 1);301 302 return 0;303}304 305static u8 clk_sam9x5_slow_get_parent(struct clk_hw *hw)306{307 struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);308 309 return !!(readl(slowck->sckcr) & slowck->bits->cr_oscsel);310}311 312static const struct clk_ops sam9x5_slow_ops = {313 .determine_rate = clk_hw_determine_rate_no_reparent,314 .set_parent = clk_sam9x5_slow_set_parent,315 .get_parent = clk_sam9x5_slow_get_parent,316};317 318static struct clk_hw * __init319at91_clk_register_sam9x5_slow(void __iomem *sckcr,320 const char *name,321 const struct clk_hw **parent_hws,322 int num_parents,323 const struct clk_slow_bits *bits)324{325 struct clk_sam9x5_slow *slowck;326 struct clk_hw *hw;327 struct clk_init_data init = {};328 int ret;329 330 if (!sckcr || !name || !parent_hws || !num_parents)331 return ERR_PTR(-EINVAL);332 333 slowck = kzalloc(sizeof(*slowck), GFP_KERNEL);334 if (!slowck)335 return ERR_PTR(-ENOMEM);336 337 init.name = name;338 init.ops = &sam9x5_slow_ops;339 init.parent_hws = parent_hws;340 init.num_parents = num_parents;341 init.flags = 0;342 343 slowck->hw.init = &init;344 slowck->sckcr = sckcr;345 slowck->bits = bits;346 slowck->parent = !!(readl(sckcr) & slowck->bits->cr_oscsel);347 348 hw = &slowck->hw;349 ret = clk_hw_register(NULL, &slowck->hw);350 if (ret) {351 kfree(slowck);352 hw = ERR_PTR(ret);353 }354 355 return hw;356}357 358static void at91_clk_unregister_sam9x5_slow(struct clk_hw *hw)359{360 struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);361 362 clk_hw_unregister(hw);363 kfree(slowck);364}365 366static void __init at91sam9x5_sckc_register(struct device_node *np,367 unsigned int rc_osc_startup_us,368 const struct clk_slow_bits *bits)369{370 void __iomem *regbase = of_iomap(np, 0);371 struct device_node *child = NULL;372 const char *xtal_name;373 struct clk_hw *slow_rc, *slow_osc, *slowck;374 static struct clk_parent_data parent_data = {375 .name = "slow_xtal",376 };377 const struct clk_hw *parent_hws[2];378 bool bypass;379 int ret;380 381 if (!regbase)382 return;383 384 slow_rc = at91_clk_register_slow_rc_osc(regbase, "slow_rc_osc",385 32768, 50000000,386 rc_osc_startup_us, bits);387 if (IS_ERR(slow_rc))388 return;389 390 xtal_name = of_clk_get_parent_name(np, 0);391 if (!xtal_name) {392 /* DT backward compatibility */393 child = of_get_compatible_child(np, "atmel,at91sam9x5-clk-slow-osc");394 if (!child)395 goto unregister_slow_rc;396 397 xtal_name = of_clk_get_parent_name(child, 0);398 bypass = of_property_read_bool(child, "atmel,osc-bypass");399 400 child = of_get_compatible_child(np, "atmel,at91sam9x5-clk-slow");401 } else {402 bypass = of_property_read_bool(np, "atmel,osc-bypass");403 }404 405 if (!xtal_name)406 goto unregister_slow_rc;407 408 parent_data.fw_name = xtal_name;409 410 slow_osc = at91_clk_register_slow_osc(regbase, "slow_osc",411 &parent_data, 1200000, bypass, bits);412 if (IS_ERR(slow_osc))413 goto unregister_slow_rc;414 415 parent_hws[0] = slow_rc;416 parent_hws[1] = slow_osc;417 slowck = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_hws,418 2, bits);419 if (IS_ERR(slowck))420 goto unregister_slow_osc;421 422 /* DT backward compatibility */423 if (child)424 ret = of_clk_add_hw_provider(child, of_clk_hw_simple_get,425 slowck);426 else427 ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, slowck);428 429 if (WARN_ON(ret))430 goto unregister_slowck;431 432 return;433 434unregister_slowck:435 at91_clk_unregister_sam9x5_slow(slowck);436unregister_slow_osc:437 at91_clk_unregister_slow_osc(slow_osc);438unregister_slow_rc:439 at91_clk_unregister_slow_rc_osc(slow_rc);440}441 442static const struct clk_slow_bits at91sam9x5_bits = {443 .cr_rcen = BIT(0),444 .cr_osc32en = BIT(1),445 .cr_osc32byp = BIT(2),446 .cr_oscsel = BIT(3),447};448 449static void __init of_at91sam9x5_sckc_setup(struct device_node *np)450{451 at91sam9x5_sckc_register(np, 75, &at91sam9x5_bits);452}453CLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc",454 of_at91sam9x5_sckc_setup);455 456static void __init of_sama5d3_sckc_setup(struct device_node *np)457{458 at91sam9x5_sckc_register(np, 500, &at91sam9x5_bits);459}460CLK_OF_DECLARE(sama5d3_clk_sckc, "atmel,sama5d3-sckc",461 of_sama5d3_sckc_setup);462 463static const struct clk_slow_bits at91sam9x60_bits = {464 .cr_osc32en = BIT(1),465 .cr_osc32byp = BIT(2),466 .cr_oscsel = BIT(24),467};468 469static void __init of_sam9x60_sckc_setup(struct device_node *np)470{471 void __iomem *regbase = of_iomap(np, 0);472 struct clk_hw_onecell_data *clk_data;473 struct clk_hw *slow_rc, *slow_osc;474 const char *xtal_name;475 const struct clk_hw *parent_hws[2];476 static struct clk_parent_data parent_data = {477 .name = "slow_xtal",478 };479 bool bypass;480 int ret;481 482 if (!regbase)483 return;484 485 slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL, "slow_rc_osc",486 NULL, 0, 32768,487 93750000);488 if (IS_ERR(slow_rc))489 return;490 491 xtal_name = of_clk_get_parent_name(np, 0);492 if (!xtal_name)493 goto unregister_slow_rc;494 495 parent_data.fw_name = xtal_name;496 bypass = of_property_read_bool(np, "atmel,osc-bypass");497 slow_osc = at91_clk_register_slow_osc(regbase, "slow_osc",498 &parent_data, 5000000, bypass,499 &at91sam9x60_bits);500 if (IS_ERR(slow_osc))501 goto unregister_slow_rc;502 503 clk_data = kzalloc(struct_size(clk_data, hws, 2), GFP_KERNEL);504 if (!clk_data)505 goto unregister_slow_osc;506 507 /* MD_SLCK and TD_SLCK. */508 clk_data->num = 2;509 clk_data->hws[0] = clk_hw_register_fixed_rate_parent_hw(NULL, "md_slck",510 slow_rc,511 0, 32768);512 if (IS_ERR(clk_data->hws[0]))513 goto clk_data_free;514 515 parent_hws[0] = slow_rc;516 parent_hws[1] = slow_osc;517 clk_data->hws[1] = at91_clk_register_sam9x5_slow(regbase, "td_slck",518 parent_hws, 2,519 &at91sam9x60_bits);520 if (IS_ERR(clk_data->hws[1]))521 goto unregister_md_slck;522 523 ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);524 if (WARN_ON(ret))525 goto unregister_td_slck;526 527 return;528 529unregister_td_slck:530 at91_clk_unregister_sam9x5_slow(clk_data->hws[1]);531unregister_md_slck:532 clk_hw_unregister(clk_data->hws[0]);533clk_data_free:534 kfree(clk_data);535unregister_slow_osc:536 at91_clk_unregister_slow_osc(slow_osc);537unregister_slow_rc:538 clk_hw_unregister(slow_rc);539}540CLK_OF_DECLARE(sam9x60_clk_sckc, "microchip,sam9x60-sckc",541 of_sam9x60_sckc_setup);542 543static int clk_sama5d4_slow_osc_prepare(struct clk_hw *hw)544{545 struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw);546 547 if (osc->prepared)548 return 0;549 550 /*551 * Assume that if it has already been selected (for example by the552 * bootloader), enough time has already passed.553 */554 if ((readl(osc->sckcr) & osc->bits->cr_oscsel)) {555 osc->prepared = true;556 return 0;557 }558 559 if (system_state < SYSTEM_RUNNING)560 udelay(osc->startup_usec);561 else562 usleep_range(osc->startup_usec, osc->startup_usec + 1);563 osc->prepared = true;564 565 return 0;566}567 568static int clk_sama5d4_slow_osc_is_prepared(struct clk_hw *hw)569{570 struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw);571 572 return osc->prepared;573}574 575static const struct clk_ops sama5d4_slow_osc_ops = {576 .prepare = clk_sama5d4_slow_osc_prepare,577 .is_prepared = clk_sama5d4_slow_osc_is_prepared,578};579 580static const struct clk_slow_bits at91sama5d4_bits = {581 .cr_oscsel = BIT(3),582};583 584static void __init of_sama5d4_sckc_setup(struct device_node *np)585{586 void __iomem *regbase = of_iomap(np, 0);587 struct clk_hw *slow_rc, *slowck;588 struct clk_sama5d4_slow_osc *osc;589 struct clk_init_data init = {};590 const char *xtal_name;591 const struct clk_hw *parent_hws[2];592 static struct clk_parent_data parent_data = {593 .name = "slow_xtal",594 };595 int ret;596 597 if (!regbase)598 return;599 600 slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL,601 "slow_rc_osc",602 NULL, 0, 32768,603 250000000);604 if (IS_ERR(slow_rc))605 return;606 607 xtal_name = of_clk_get_parent_name(np, 0);608 if (!xtal_name)609 goto unregister_slow_rc;610 parent_data.fw_name = xtal_name;611 612 osc = kzalloc(sizeof(*osc), GFP_KERNEL);613 if (!osc)614 goto unregister_slow_rc;615 616 init.name = "slow_osc";617 init.ops = &sama5d4_slow_osc_ops;618 init.parent_data = &parent_data;619 init.num_parents = 1;620 init.flags = CLK_IGNORE_UNUSED;621 622 osc->hw.init = &init;623 osc->sckcr = regbase;624 osc->startup_usec = 1200000;625 osc->bits = &at91sama5d4_bits;626 627 ret = clk_hw_register(NULL, &osc->hw);628 if (ret)629 goto free_slow_osc_data;630 631 parent_hws[0] = slow_rc;632 parent_hws[1] = &osc->hw;633 slowck = at91_clk_register_sam9x5_slow(regbase, "slowck",634 parent_hws, 2,635 &at91sama5d4_bits);636 if (IS_ERR(slowck))637 goto unregister_slow_osc;638 639 ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, slowck);640 if (WARN_ON(ret))641 goto unregister_slowck;642 643 return;644 645unregister_slowck:646 at91_clk_unregister_sam9x5_slow(slowck);647unregister_slow_osc:648 clk_hw_unregister(&osc->hw);649free_slow_osc_data:650 kfree(osc);651unregister_slow_rc:652 clk_hw_unregister(slow_rc);653}654CLK_OF_DECLARE(sama5d4_clk_sckc, "atmel,sama5d4-sckc",655 of_sama5d4_sckc_setup);656