18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/clk/at91/sckc.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 98c2ecf20Sopenharmony_ci#include <linux/clkdev.h> 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/of.h> 128c2ecf20Sopenharmony_ci#include <linux/of_address.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define SLOW_CLOCK_FREQ 32768 168c2ecf20Sopenharmony_ci#define SLOWCK_SW_CYCLES 5 178c2ecf20Sopenharmony_ci#define SLOWCK_SW_TIME_USEC ((SLOWCK_SW_CYCLES * USEC_PER_SEC) / \ 188c2ecf20Sopenharmony_ci SLOW_CLOCK_FREQ) 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define AT91_SCKC_CR 0x00 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistruct clk_slow_bits { 238c2ecf20Sopenharmony_ci u32 cr_rcen; 248c2ecf20Sopenharmony_ci u32 cr_osc32en; 258c2ecf20Sopenharmony_ci u32 cr_osc32byp; 268c2ecf20Sopenharmony_ci u32 cr_oscsel; 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistruct clk_slow_osc { 308c2ecf20Sopenharmony_ci struct clk_hw hw; 318c2ecf20Sopenharmony_ci void __iomem *sckcr; 328c2ecf20Sopenharmony_ci const struct clk_slow_bits *bits; 338c2ecf20Sopenharmony_ci unsigned long startup_usec; 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define to_clk_slow_osc(hw) container_of(hw, struct clk_slow_osc, hw) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistruct clk_sama5d4_slow_osc { 398c2ecf20Sopenharmony_ci struct clk_hw hw; 408c2ecf20Sopenharmony_ci void __iomem *sckcr; 418c2ecf20Sopenharmony_ci const struct clk_slow_bits *bits; 428c2ecf20Sopenharmony_ci unsigned long startup_usec; 438c2ecf20Sopenharmony_ci bool prepared; 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define to_clk_sama5d4_slow_osc(hw) container_of(hw, struct clk_sama5d4_slow_osc, hw) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistruct clk_slow_rc_osc { 498c2ecf20Sopenharmony_ci struct clk_hw hw; 508c2ecf20Sopenharmony_ci void __iomem *sckcr; 518c2ecf20Sopenharmony_ci const struct clk_slow_bits *bits; 528c2ecf20Sopenharmony_ci unsigned long frequency; 538c2ecf20Sopenharmony_ci unsigned long accuracy; 548c2ecf20Sopenharmony_ci unsigned long startup_usec; 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define to_clk_slow_rc_osc(hw) container_of(hw, struct clk_slow_rc_osc, hw) 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistruct clk_sam9x5_slow { 608c2ecf20Sopenharmony_ci struct clk_hw hw; 618c2ecf20Sopenharmony_ci void __iomem *sckcr; 628c2ecf20Sopenharmony_ci const struct clk_slow_bits *bits; 638c2ecf20Sopenharmony_ci u8 parent; 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, hw) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic int clk_slow_osc_prepare(struct clk_hw *hw) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct clk_slow_osc *osc = to_clk_slow_osc(hw); 718c2ecf20Sopenharmony_ci void __iomem *sckcr = osc->sckcr; 728c2ecf20Sopenharmony_ci u32 tmp = readl(sckcr); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (tmp & (osc->bits->cr_osc32byp | osc->bits->cr_osc32en)) 758c2ecf20Sopenharmony_ci return 0; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci writel(tmp | osc->bits->cr_osc32en, sckcr); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (system_state < SYSTEM_RUNNING) 808c2ecf20Sopenharmony_ci udelay(osc->startup_usec); 818c2ecf20Sopenharmony_ci else 828c2ecf20Sopenharmony_ci usleep_range(osc->startup_usec, osc->startup_usec + 1); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci return 0; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic void clk_slow_osc_unprepare(struct clk_hw *hw) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci struct clk_slow_osc *osc = to_clk_slow_osc(hw); 908c2ecf20Sopenharmony_ci void __iomem *sckcr = osc->sckcr; 918c2ecf20Sopenharmony_ci u32 tmp = readl(sckcr); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (tmp & osc->bits->cr_osc32byp) 948c2ecf20Sopenharmony_ci return; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci writel(tmp & ~osc->bits->cr_osc32en, sckcr); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic int clk_slow_osc_is_prepared(struct clk_hw *hw) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct clk_slow_osc *osc = to_clk_slow_osc(hw); 1028c2ecf20Sopenharmony_ci void __iomem *sckcr = osc->sckcr; 1038c2ecf20Sopenharmony_ci u32 tmp = readl(sckcr); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (tmp & osc->bits->cr_osc32byp) 1068c2ecf20Sopenharmony_ci return 1; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return !!(tmp & osc->bits->cr_osc32en); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic const struct clk_ops slow_osc_ops = { 1128c2ecf20Sopenharmony_ci .prepare = clk_slow_osc_prepare, 1138c2ecf20Sopenharmony_ci .unprepare = clk_slow_osc_unprepare, 1148c2ecf20Sopenharmony_ci .is_prepared = clk_slow_osc_is_prepared, 1158c2ecf20Sopenharmony_ci}; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic struct clk_hw * __init 1188c2ecf20Sopenharmony_ciat91_clk_register_slow_osc(void __iomem *sckcr, 1198c2ecf20Sopenharmony_ci const char *name, 1208c2ecf20Sopenharmony_ci const char *parent_name, 1218c2ecf20Sopenharmony_ci unsigned long startup, 1228c2ecf20Sopenharmony_ci bool bypass, 1238c2ecf20Sopenharmony_ci const struct clk_slow_bits *bits) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct clk_slow_osc *osc; 1268c2ecf20Sopenharmony_ci struct clk_hw *hw; 1278c2ecf20Sopenharmony_ci struct clk_init_data init; 1288c2ecf20Sopenharmony_ci int ret; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (!sckcr || !name || !parent_name) 1318c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci osc = kzalloc(sizeof(*osc), GFP_KERNEL); 1348c2ecf20Sopenharmony_ci if (!osc) 1358c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci init.name = name; 1388c2ecf20Sopenharmony_ci init.ops = &slow_osc_ops; 1398c2ecf20Sopenharmony_ci init.parent_names = &parent_name; 1408c2ecf20Sopenharmony_ci init.num_parents = 1; 1418c2ecf20Sopenharmony_ci init.flags = CLK_IGNORE_UNUSED; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci osc->hw.init = &init; 1448c2ecf20Sopenharmony_ci osc->sckcr = sckcr; 1458c2ecf20Sopenharmony_ci osc->startup_usec = startup; 1468c2ecf20Sopenharmony_ci osc->bits = bits; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (bypass) 1498c2ecf20Sopenharmony_ci writel((readl(sckcr) & ~osc->bits->cr_osc32en) | 1508c2ecf20Sopenharmony_ci osc->bits->cr_osc32byp, sckcr); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci hw = &osc->hw; 1538c2ecf20Sopenharmony_ci ret = clk_hw_register(NULL, &osc->hw); 1548c2ecf20Sopenharmony_ci if (ret) { 1558c2ecf20Sopenharmony_ci kfree(osc); 1568c2ecf20Sopenharmony_ci hw = ERR_PTR(ret); 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return hw; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic void at91_clk_unregister_slow_osc(struct clk_hw *hw) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci struct clk_slow_osc *osc = to_clk_slow_osc(hw); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci clk_hw_unregister(hw); 1678c2ecf20Sopenharmony_ci kfree(osc); 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw, 1718c2ecf20Sopenharmony_ci unsigned long parent_rate) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return osc->frequency; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic unsigned long clk_slow_rc_osc_recalc_accuracy(struct clk_hw *hw, 1798c2ecf20Sopenharmony_ci unsigned long parent_acc) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci return osc->accuracy; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic int clk_slow_rc_osc_prepare(struct clk_hw *hw) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 1898c2ecf20Sopenharmony_ci void __iomem *sckcr = osc->sckcr; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci writel(readl(sckcr) | osc->bits->cr_rcen, sckcr); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (system_state < SYSTEM_RUNNING) 1948c2ecf20Sopenharmony_ci udelay(osc->startup_usec); 1958c2ecf20Sopenharmony_ci else 1968c2ecf20Sopenharmony_ci usleep_range(osc->startup_usec, osc->startup_usec + 1); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci return 0; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic void clk_slow_rc_osc_unprepare(struct clk_hw *hw) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 2048c2ecf20Sopenharmony_ci void __iomem *sckcr = osc->sckcr; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci writel(readl(sckcr) & ~osc->bits->cr_rcen, sckcr); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic int clk_slow_rc_osc_is_prepared(struct clk_hw *hw) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci return !!(readl(osc->sckcr) & osc->bits->cr_rcen); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic const struct clk_ops slow_rc_osc_ops = { 2178c2ecf20Sopenharmony_ci .prepare = clk_slow_rc_osc_prepare, 2188c2ecf20Sopenharmony_ci .unprepare = clk_slow_rc_osc_unprepare, 2198c2ecf20Sopenharmony_ci .is_prepared = clk_slow_rc_osc_is_prepared, 2208c2ecf20Sopenharmony_ci .recalc_rate = clk_slow_rc_osc_recalc_rate, 2218c2ecf20Sopenharmony_ci .recalc_accuracy = clk_slow_rc_osc_recalc_accuracy, 2228c2ecf20Sopenharmony_ci}; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic struct clk_hw * __init 2258c2ecf20Sopenharmony_ciat91_clk_register_slow_rc_osc(void __iomem *sckcr, 2268c2ecf20Sopenharmony_ci const char *name, 2278c2ecf20Sopenharmony_ci unsigned long frequency, 2288c2ecf20Sopenharmony_ci unsigned long accuracy, 2298c2ecf20Sopenharmony_ci unsigned long startup, 2308c2ecf20Sopenharmony_ci const struct clk_slow_bits *bits) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci struct clk_slow_rc_osc *osc; 2338c2ecf20Sopenharmony_ci struct clk_hw *hw; 2348c2ecf20Sopenharmony_ci struct clk_init_data init; 2358c2ecf20Sopenharmony_ci int ret; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (!sckcr || !name) 2388c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci osc = kzalloc(sizeof(*osc), GFP_KERNEL); 2418c2ecf20Sopenharmony_ci if (!osc) 2428c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci init.name = name; 2458c2ecf20Sopenharmony_ci init.ops = &slow_rc_osc_ops; 2468c2ecf20Sopenharmony_ci init.parent_names = NULL; 2478c2ecf20Sopenharmony_ci init.num_parents = 0; 2488c2ecf20Sopenharmony_ci init.flags = CLK_IGNORE_UNUSED; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci osc->hw.init = &init; 2518c2ecf20Sopenharmony_ci osc->sckcr = sckcr; 2528c2ecf20Sopenharmony_ci osc->bits = bits; 2538c2ecf20Sopenharmony_ci osc->frequency = frequency; 2548c2ecf20Sopenharmony_ci osc->accuracy = accuracy; 2558c2ecf20Sopenharmony_ci osc->startup_usec = startup; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci hw = &osc->hw; 2588c2ecf20Sopenharmony_ci ret = clk_hw_register(NULL, &osc->hw); 2598c2ecf20Sopenharmony_ci if (ret) { 2608c2ecf20Sopenharmony_ci kfree(osc); 2618c2ecf20Sopenharmony_ci hw = ERR_PTR(ret); 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci return hw; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic void at91_clk_unregister_slow_rc_osc(struct clk_hw *hw) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci clk_hw_unregister(hw); 2728c2ecf20Sopenharmony_ci kfree(osc); 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); 2788c2ecf20Sopenharmony_ci void __iomem *sckcr = slowck->sckcr; 2798c2ecf20Sopenharmony_ci u32 tmp; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (index > 1) 2828c2ecf20Sopenharmony_ci return -EINVAL; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci tmp = readl(sckcr); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if ((!index && !(tmp & slowck->bits->cr_oscsel)) || 2878c2ecf20Sopenharmony_ci (index && (tmp & slowck->bits->cr_oscsel))) 2888c2ecf20Sopenharmony_ci return 0; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if (index) 2918c2ecf20Sopenharmony_ci tmp |= slowck->bits->cr_oscsel; 2928c2ecf20Sopenharmony_ci else 2938c2ecf20Sopenharmony_ci tmp &= ~slowck->bits->cr_oscsel; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci writel(tmp, sckcr); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (system_state < SYSTEM_RUNNING) 2988c2ecf20Sopenharmony_ci udelay(SLOWCK_SW_TIME_USEC); 2998c2ecf20Sopenharmony_ci else 3008c2ecf20Sopenharmony_ci usleep_range(SLOWCK_SW_TIME_USEC, SLOWCK_SW_TIME_USEC + 1); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci return 0; 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic u8 clk_sam9x5_slow_get_parent(struct clk_hw *hw) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci return !!(readl(slowck->sckcr) & slowck->bits->cr_oscsel); 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic const struct clk_ops sam9x5_slow_ops = { 3138c2ecf20Sopenharmony_ci .set_parent = clk_sam9x5_slow_set_parent, 3148c2ecf20Sopenharmony_ci .get_parent = clk_sam9x5_slow_get_parent, 3158c2ecf20Sopenharmony_ci}; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic struct clk_hw * __init 3188c2ecf20Sopenharmony_ciat91_clk_register_sam9x5_slow(void __iomem *sckcr, 3198c2ecf20Sopenharmony_ci const char *name, 3208c2ecf20Sopenharmony_ci const char **parent_names, 3218c2ecf20Sopenharmony_ci int num_parents, 3228c2ecf20Sopenharmony_ci const struct clk_slow_bits *bits) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci struct clk_sam9x5_slow *slowck; 3258c2ecf20Sopenharmony_ci struct clk_hw *hw; 3268c2ecf20Sopenharmony_ci struct clk_init_data init; 3278c2ecf20Sopenharmony_ci int ret; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (!sckcr || !name || !parent_names || !num_parents) 3308c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci slowck = kzalloc(sizeof(*slowck), GFP_KERNEL); 3338c2ecf20Sopenharmony_ci if (!slowck) 3348c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci init.name = name; 3378c2ecf20Sopenharmony_ci init.ops = &sam9x5_slow_ops; 3388c2ecf20Sopenharmony_ci init.parent_names = parent_names; 3398c2ecf20Sopenharmony_ci init.num_parents = num_parents; 3408c2ecf20Sopenharmony_ci init.flags = 0; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci slowck->hw.init = &init; 3438c2ecf20Sopenharmony_ci slowck->sckcr = sckcr; 3448c2ecf20Sopenharmony_ci slowck->bits = bits; 3458c2ecf20Sopenharmony_ci slowck->parent = !!(readl(sckcr) & slowck->bits->cr_oscsel); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci hw = &slowck->hw; 3488c2ecf20Sopenharmony_ci ret = clk_hw_register(NULL, &slowck->hw); 3498c2ecf20Sopenharmony_ci if (ret) { 3508c2ecf20Sopenharmony_ci kfree(slowck); 3518c2ecf20Sopenharmony_ci hw = ERR_PTR(ret); 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci return hw; 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic void at91_clk_unregister_sam9x5_slow(struct clk_hw *hw) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci clk_hw_unregister(hw); 3628c2ecf20Sopenharmony_ci kfree(slowck); 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic void __init at91sam9x5_sckc_register(struct device_node *np, 3668c2ecf20Sopenharmony_ci unsigned int rc_osc_startup_us, 3678c2ecf20Sopenharmony_ci const struct clk_slow_bits *bits) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci const char *parent_names[2] = { "slow_rc_osc", "slow_osc" }; 3708c2ecf20Sopenharmony_ci void __iomem *regbase = of_iomap(np, 0); 3718c2ecf20Sopenharmony_ci struct device_node *child = NULL; 3728c2ecf20Sopenharmony_ci const char *xtal_name; 3738c2ecf20Sopenharmony_ci struct clk_hw *slow_rc, *slow_osc, *slowck; 3748c2ecf20Sopenharmony_ci bool bypass; 3758c2ecf20Sopenharmony_ci int ret; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci if (!regbase) 3788c2ecf20Sopenharmony_ci return; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci slow_rc = at91_clk_register_slow_rc_osc(regbase, parent_names[0], 3818c2ecf20Sopenharmony_ci 32768, 50000000, 3828c2ecf20Sopenharmony_ci rc_osc_startup_us, bits); 3838c2ecf20Sopenharmony_ci if (IS_ERR(slow_rc)) 3848c2ecf20Sopenharmony_ci return; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci xtal_name = of_clk_get_parent_name(np, 0); 3878c2ecf20Sopenharmony_ci if (!xtal_name) { 3888c2ecf20Sopenharmony_ci /* DT backward compatibility */ 3898c2ecf20Sopenharmony_ci child = of_get_compatible_child(np, "atmel,at91sam9x5-clk-slow-osc"); 3908c2ecf20Sopenharmony_ci if (!child) 3918c2ecf20Sopenharmony_ci goto unregister_slow_rc; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci xtal_name = of_clk_get_parent_name(child, 0); 3948c2ecf20Sopenharmony_ci bypass = of_property_read_bool(child, "atmel,osc-bypass"); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci child = of_get_compatible_child(np, "atmel,at91sam9x5-clk-slow"); 3978c2ecf20Sopenharmony_ci } else { 3988c2ecf20Sopenharmony_ci bypass = of_property_read_bool(np, "atmel,osc-bypass"); 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci if (!xtal_name) 4028c2ecf20Sopenharmony_ci goto unregister_slow_rc; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci slow_osc = at91_clk_register_slow_osc(regbase, parent_names[1], 4058c2ecf20Sopenharmony_ci xtal_name, 1200000, bypass, bits); 4068c2ecf20Sopenharmony_ci if (IS_ERR(slow_osc)) 4078c2ecf20Sopenharmony_ci goto unregister_slow_rc; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci slowck = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_names, 4108c2ecf20Sopenharmony_ci 2, bits); 4118c2ecf20Sopenharmony_ci if (IS_ERR(slowck)) 4128c2ecf20Sopenharmony_ci goto unregister_slow_osc; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* DT backward compatibility */ 4158c2ecf20Sopenharmony_ci if (child) 4168c2ecf20Sopenharmony_ci ret = of_clk_add_hw_provider(child, of_clk_hw_simple_get, 4178c2ecf20Sopenharmony_ci slowck); 4188c2ecf20Sopenharmony_ci else 4198c2ecf20Sopenharmony_ci ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, slowck); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (WARN_ON(ret)) 4228c2ecf20Sopenharmony_ci goto unregister_slowck; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci return; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ciunregister_slowck: 4278c2ecf20Sopenharmony_ci at91_clk_unregister_sam9x5_slow(slowck); 4288c2ecf20Sopenharmony_ciunregister_slow_osc: 4298c2ecf20Sopenharmony_ci at91_clk_unregister_slow_osc(slow_osc); 4308c2ecf20Sopenharmony_ciunregister_slow_rc: 4318c2ecf20Sopenharmony_ci at91_clk_unregister_slow_rc_osc(slow_rc); 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic const struct clk_slow_bits at91sam9x5_bits = { 4358c2ecf20Sopenharmony_ci .cr_rcen = BIT(0), 4368c2ecf20Sopenharmony_ci .cr_osc32en = BIT(1), 4378c2ecf20Sopenharmony_ci .cr_osc32byp = BIT(2), 4388c2ecf20Sopenharmony_ci .cr_oscsel = BIT(3), 4398c2ecf20Sopenharmony_ci}; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cistatic void __init of_at91sam9x5_sckc_setup(struct device_node *np) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci at91sam9x5_sckc_register(np, 75, &at91sam9x5_bits); 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ciCLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc", 4468c2ecf20Sopenharmony_ci of_at91sam9x5_sckc_setup); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic void __init of_sama5d3_sckc_setup(struct device_node *np) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci at91sam9x5_sckc_register(np, 500, &at91sam9x5_bits); 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ciCLK_OF_DECLARE(sama5d3_clk_sckc, "atmel,sama5d3-sckc", 4538c2ecf20Sopenharmony_ci of_sama5d3_sckc_setup); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cistatic const struct clk_slow_bits at91sam9x60_bits = { 4568c2ecf20Sopenharmony_ci .cr_osc32en = BIT(1), 4578c2ecf20Sopenharmony_ci .cr_osc32byp = BIT(2), 4588c2ecf20Sopenharmony_ci .cr_oscsel = BIT(24), 4598c2ecf20Sopenharmony_ci}; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic void __init of_sam9x60_sckc_setup(struct device_node *np) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci void __iomem *regbase = of_iomap(np, 0); 4648c2ecf20Sopenharmony_ci struct clk_hw_onecell_data *clk_data; 4658c2ecf20Sopenharmony_ci struct clk_hw *slow_rc, *slow_osc; 4668c2ecf20Sopenharmony_ci const char *xtal_name; 4678c2ecf20Sopenharmony_ci const char *parent_names[2] = { "slow_rc_osc", "slow_osc" }; 4688c2ecf20Sopenharmony_ci bool bypass; 4698c2ecf20Sopenharmony_ci int ret; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if (!regbase) 4728c2ecf20Sopenharmony_ci return; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL, parent_names[0], 4758c2ecf20Sopenharmony_ci NULL, 0, 32768, 4768c2ecf20Sopenharmony_ci 93750000); 4778c2ecf20Sopenharmony_ci if (IS_ERR(slow_rc)) 4788c2ecf20Sopenharmony_ci return; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci xtal_name = of_clk_get_parent_name(np, 0); 4818c2ecf20Sopenharmony_ci if (!xtal_name) 4828c2ecf20Sopenharmony_ci goto unregister_slow_rc; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci bypass = of_property_read_bool(np, "atmel,osc-bypass"); 4858c2ecf20Sopenharmony_ci slow_osc = at91_clk_register_slow_osc(regbase, parent_names[1], 4868c2ecf20Sopenharmony_ci xtal_name, 5000000, bypass, 4878c2ecf20Sopenharmony_ci &at91sam9x60_bits); 4888c2ecf20Sopenharmony_ci if (IS_ERR(slow_osc)) 4898c2ecf20Sopenharmony_ci goto unregister_slow_rc; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci clk_data = kzalloc(struct_size(clk_data, hws, 2), GFP_KERNEL); 4928c2ecf20Sopenharmony_ci if (!clk_data) 4938c2ecf20Sopenharmony_ci goto unregister_slow_osc; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci /* MD_SLCK and TD_SLCK. */ 4968c2ecf20Sopenharmony_ci clk_data->num = 2; 4978c2ecf20Sopenharmony_ci clk_data->hws[0] = clk_hw_register_fixed_rate(NULL, "md_slck", 4988c2ecf20Sopenharmony_ci parent_names[0], 4998c2ecf20Sopenharmony_ci 0, 32768); 5008c2ecf20Sopenharmony_ci if (IS_ERR(clk_data->hws[0])) 5018c2ecf20Sopenharmony_ci goto clk_data_free; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci clk_data->hws[1] = at91_clk_register_sam9x5_slow(regbase, "td_slck", 5048c2ecf20Sopenharmony_ci parent_names, 2, 5058c2ecf20Sopenharmony_ci &at91sam9x60_bits); 5068c2ecf20Sopenharmony_ci if (IS_ERR(clk_data->hws[1])) 5078c2ecf20Sopenharmony_ci goto unregister_md_slck; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data); 5108c2ecf20Sopenharmony_ci if (WARN_ON(ret)) 5118c2ecf20Sopenharmony_ci goto unregister_td_slck; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci return; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ciunregister_td_slck: 5168c2ecf20Sopenharmony_ci at91_clk_unregister_sam9x5_slow(clk_data->hws[1]); 5178c2ecf20Sopenharmony_ciunregister_md_slck: 5188c2ecf20Sopenharmony_ci clk_hw_unregister(clk_data->hws[0]); 5198c2ecf20Sopenharmony_ciclk_data_free: 5208c2ecf20Sopenharmony_ci kfree(clk_data); 5218c2ecf20Sopenharmony_ciunregister_slow_osc: 5228c2ecf20Sopenharmony_ci at91_clk_unregister_slow_osc(slow_osc); 5238c2ecf20Sopenharmony_ciunregister_slow_rc: 5248c2ecf20Sopenharmony_ci clk_hw_unregister(slow_rc); 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ciCLK_OF_DECLARE(sam9x60_clk_sckc, "microchip,sam9x60-sckc", 5278c2ecf20Sopenharmony_ci of_sam9x60_sckc_setup); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic int clk_sama5d4_slow_osc_prepare(struct clk_hw *hw) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci if (osc->prepared) 5348c2ecf20Sopenharmony_ci return 0; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci /* 5378c2ecf20Sopenharmony_ci * Assume that if it has already been selected (for example by the 5388c2ecf20Sopenharmony_ci * bootloader), enough time has aready passed. 5398c2ecf20Sopenharmony_ci */ 5408c2ecf20Sopenharmony_ci if ((readl(osc->sckcr) & osc->bits->cr_oscsel)) { 5418c2ecf20Sopenharmony_ci osc->prepared = true; 5428c2ecf20Sopenharmony_ci return 0; 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci if (system_state < SYSTEM_RUNNING) 5468c2ecf20Sopenharmony_ci udelay(osc->startup_usec); 5478c2ecf20Sopenharmony_ci else 5488c2ecf20Sopenharmony_ci usleep_range(osc->startup_usec, osc->startup_usec + 1); 5498c2ecf20Sopenharmony_ci osc->prepared = true; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci return 0; 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistatic int clk_sama5d4_slow_osc_is_prepared(struct clk_hw *hw) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci return osc->prepared; 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cistatic const struct clk_ops sama5d4_slow_osc_ops = { 5628c2ecf20Sopenharmony_ci .prepare = clk_sama5d4_slow_osc_prepare, 5638c2ecf20Sopenharmony_ci .is_prepared = clk_sama5d4_slow_osc_is_prepared, 5648c2ecf20Sopenharmony_ci}; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic const struct clk_slow_bits at91sama5d4_bits = { 5678c2ecf20Sopenharmony_ci .cr_oscsel = BIT(3), 5688c2ecf20Sopenharmony_ci}; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_cistatic void __init of_sama5d4_sckc_setup(struct device_node *np) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci void __iomem *regbase = of_iomap(np, 0); 5738c2ecf20Sopenharmony_ci struct clk_hw *slow_rc, *slowck; 5748c2ecf20Sopenharmony_ci struct clk_sama5d4_slow_osc *osc; 5758c2ecf20Sopenharmony_ci struct clk_init_data init; 5768c2ecf20Sopenharmony_ci const char *xtal_name; 5778c2ecf20Sopenharmony_ci const char *parent_names[2] = { "slow_rc_osc", "slow_osc" }; 5788c2ecf20Sopenharmony_ci int ret; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci if (!regbase) 5818c2ecf20Sopenharmony_ci return; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL, 5848c2ecf20Sopenharmony_ci parent_names[0], 5858c2ecf20Sopenharmony_ci NULL, 0, 32768, 5868c2ecf20Sopenharmony_ci 250000000); 5878c2ecf20Sopenharmony_ci if (IS_ERR(slow_rc)) 5888c2ecf20Sopenharmony_ci return; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci xtal_name = of_clk_get_parent_name(np, 0); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci osc = kzalloc(sizeof(*osc), GFP_KERNEL); 5938c2ecf20Sopenharmony_ci if (!osc) 5948c2ecf20Sopenharmony_ci goto unregister_slow_rc; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci init.name = parent_names[1]; 5978c2ecf20Sopenharmony_ci init.ops = &sama5d4_slow_osc_ops; 5988c2ecf20Sopenharmony_ci init.parent_names = &xtal_name; 5998c2ecf20Sopenharmony_ci init.num_parents = 1; 6008c2ecf20Sopenharmony_ci init.flags = CLK_IGNORE_UNUSED; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci osc->hw.init = &init; 6038c2ecf20Sopenharmony_ci osc->sckcr = regbase; 6048c2ecf20Sopenharmony_ci osc->startup_usec = 1200000; 6058c2ecf20Sopenharmony_ci osc->bits = &at91sama5d4_bits; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci ret = clk_hw_register(NULL, &osc->hw); 6088c2ecf20Sopenharmony_ci if (ret) 6098c2ecf20Sopenharmony_ci goto free_slow_osc_data; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci slowck = at91_clk_register_sam9x5_slow(regbase, "slowck", 6128c2ecf20Sopenharmony_ci parent_names, 2, 6138c2ecf20Sopenharmony_ci &at91sama5d4_bits); 6148c2ecf20Sopenharmony_ci if (IS_ERR(slowck)) 6158c2ecf20Sopenharmony_ci goto unregister_slow_osc; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, slowck); 6188c2ecf20Sopenharmony_ci if (WARN_ON(ret)) 6198c2ecf20Sopenharmony_ci goto unregister_slowck; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci return; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ciunregister_slowck: 6248c2ecf20Sopenharmony_ci at91_clk_unregister_sam9x5_slow(slowck); 6258c2ecf20Sopenharmony_ciunregister_slow_osc: 6268c2ecf20Sopenharmony_ci clk_hw_unregister(&osc->hw); 6278c2ecf20Sopenharmony_cifree_slow_osc_data: 6288c2ecf20Sopenharmony_ci kfree(osc); 6298c2ecf20Sopenharmony_ciunregister_slow_rc: 6308c2ecf20Sopenharmony_ci clk_hw_unregister(slow_rc); 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ciCLK_OF_DECLARE(sama5d4_clk_sckc, "atmel,sama5d4-sckc", 6338c2ecf20Sopenharmony_ci of_sama5d4_sckc_setup); 634