162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * drivers/clk/at91/sckc.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/clk-provider.h> 962306a36Sopenharmony_ci#include <linux/clkdev.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/of.h> 1262306a36Sopenharmony_ci#include <linux/of_address.h> 1362306a36Sopenharmony_ci#include <linux/io.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define SLOW_CLOCK_FREQ 32768 1662306a36Sopenharmony_ci#define SLOWCK_SW_CYCLES 5 1762306a36Sopenharmony_ci#define SLOWCK_SW_TIME_USEC ((SLOWCK_SW_CYCLES * USEC_PER_SEC) / \ 1862306a36Sopenharmony_ci SLOW_CLOCK_FREQ) 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define AT91_SCKC_CR 0x00 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistruct clk_slow_bits { 2362306a36Sopenharmony_ci u32 cr_rcen; 2462306a36Sopenharmony_ci u32 cr_osc32en; 2562306a36Sopenharmony_ci u32 cr_osc32byp; 2662306a36Sopenharmony_ci u32 cr_oscsel; 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistruct clk_slow_osc { 3062306a36Sopenharmony_ci struct clk_hw hw; 3162306a36Sopenharmony_ci void __iomem *sckcr; 3262306a36Sopenharmony_ci const struct clk_slow_bits *bits; 3362306a36Sopenharmony_ci unsigned long startup_usec; 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define to_clk_slow_osc(hw) container_of(hw, struct clk_slow_osc, hw) 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistruct clk_sama5d4_slow_osc { 3962306a36Sopenharmony_ci struct clk_hw hw; 4062306a36Sopenharmony_ci void __iomem *sckcr; 4162306a36Sopenharmony_ci const struct clk_slow_bits *bits; 4262306a36Sopenharmony_ci unsigned long startup_usec; 4362306a36Sopenharmony_ci bool prepared; 4462306a36Sopenharmony_ci}; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define to_clk_sama5d4_slow_osc(hw) container_of(hw, struct clk_sama5d4_slow_osc, hw) 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistruct clk_slow_rc_osc { 4962306a36Sopenharmony_ci struct clk_hw hw; 5062306a36Sopenharmony_ci void __iomem *sckcr; 5162306a36Sopenharmony_ci const struct clk_slow_bits *bits; 5262306a36Sopenharmony_ci unsigned long frequency; 5362306a36Sopenharmony_ci unsigned long accuracy; 5462306a36Sopenharmony_ci unsigned long startup_usec; 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define to_clk_slow_rc_osc(hw) container_of(hw, struct clk_slow_rc_osc, hw) 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistruct clk_sam9x5_slow { 6062306a36Sopenharmony_ci struct clk_hw hw; 6162306a36Sopenharmony_ci void __iomem *sckcr; 6262306a36Sopenharmony_ci const struct clk_slow_bits *bits; 6362306a36Sopenharmony_ci u8 parent; 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, hw) 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int clk_slow_osc_prepare(struct clk_hw *hw) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct clk_slow_osc *osc = to_clk_slow_osc(hw); 7162306a36Sopenharmony_ci void __iomem *sckcr = osc->sckcr; 7262306a36Sopenharmony_ci u32 tmp = readl(sckcr); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (tmp & (osc->bits->cr_osc32byp | osc->bits->cr_osc32en)) 7562306a36Sopenharmony_ci return 0; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci writel(tmp | osc->bits->cr_osc32en, sckcr); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (system_state < SYSTEM_RUNNING) 8062306a36Sopenharmony_ci udelay(osc->startup_usec); 8162306a36Sopenharmony_ci else 8262306a36Sopenharmony_ci usleep_range(osc->startup_usec, osc->startup_usec + 1); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return 0; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic void clk_slow_osc_unprepare(struct clk_hw *hw) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci struct clk_slow_osc *osc = to_clk_slow_osc(hw); 9062306a36Sopenharmony_ci void __iomem *sckcr = osc->sckcr; 9162306a36Sopenharmony_ci u32 tmp = readl(sckcr); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (tmp & osc->bits->cr_osc32byp) 9462306a36Sopenharmony_ci return; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci writel(tmp & ~osc->bits->cr_osc32en, sckcr); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic int clk_slow_osc_is_prepared(struct clk_hw *hw) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct clk_slow_osc *osc = to_clk_slow_osc(hw); 10262306a36Sopenharmony_ci void __iomem *sckcr = osc->sckcr; 10362306a36Sopenharmony_ci u32 tmp = readl(sckcr); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (tmp & osc->bits->cr_osc32byp) 10662306a36Sopenharmony_ci return 1; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci return !!(tmp & osc->bits->cr_osc32en); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic const struct clk_ops slow_osc_ops = { 11262306a36Sopenharmony_ci .prepare = clk_slow_osc_prepare, 11362306a36Sopenharmony_ci .unprepare = clk_slow_osc_unprepare, 11462306a36Sopenharmony_ci .is_prepared = clk_slow_osc_is_prepared, 11562306a36Sopenharmony_ci}; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic struct clk_hw * __init 11862306a36Sopenharmony_ciat91_clk_register_slow_osc(void __iomem *sckcr, 11962306a36Sopenharmony_ci const char *name, 12062306a36Sopenharmony_ci const struct clk_parent_data *parent_data, 12162306a36Sopenharmony_ci unsigned long startup, 12262306a36Sopenharmony_ci bool bypass, 12362306a36Sopenharmony_ci const struct clk_slow_bits *bits) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct clk_slow_osc *osc; 12662306a36Sopenharmony_ci struct clk_hw *hw; 12762306a36Sopenharmony_ci struct clk_init_data init = {}; 12862306a36Sopenharmony_ci int ret; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (!sckcr || !name || !parent_data) 13162306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci osc = kzalloc(sizeof(*osc), GFP_KERNEL); 13462306a36Sopenharmony_ci if (!osc) 13562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci init.name = name; 13862306a36Sopenharmony_ci init.ops = &slow_osc_ops; 13962306a36Sopenharmony_ci init.parent_data = parent_data; 14062306a36Sopenharmony_ci init.num_parents = 1; 14162306a36Sopenharmony_ci init.flags = CLK_IGNORE_UNUSED; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci osc->hw.init = &init; 14462306a36Sopenharmony_ci osc->sckcr = sckcr; 14562306a36Sopenharmony_ci osc->startup_usec = startup; 14662306a36Sopenharmony_ci osc->bits = bits; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (bypass) 14962306a36Sopenharmony_ci writel((readl(sckcr) & ~osc->bits->cr_osc32en) | 15062306a36Sopenharmony_ci osc->bits->cr_osc32byp, sckcr); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci hw = &osc->hw; 15362306a36Sopenharmony_ci ret = clk_hw_register(NULL, &osc->hw); 15462306a36Sopenharmony_ci if (ret) { 15562306a36Sopenharmony_ci kfree(osc); 15662306a36Sopenharmony_ci hw = ERR_PTR(ret); 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci return hw; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic void at91_clk_unregister_slow_osc(struct clk_hw *hw) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci struct clk_slow_osc *osc = to_clk_slow_osc(hw); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci clk_hw_unregister(hw); 16762306a36Sopenharmony_ci kfree(osc); 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw, 17162306a36Sopenharmony_ci unsigned long parent_rate) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return osc->frequency; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic unsigned long clk_slow_rc_osc_recalc_accuracy(struct clk_hw *hw, 17962306a36Sopenharmony_ci unsigned long parent_acc) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci return osc->accuracy; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic int clk_slow_rc_osc_prepare(struct clk_hw *hw) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 18962306a36Sopenharmony_ci void __iomem *sckcr = osc->sckcr; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci writel(readl(sckcr) | osc->bits->cr_rcen, sckcr); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (system_state < SYSTEM_RUNNING) 19462306a36Sopenharmony_ci udelay(osc->startup_usec); 19562306a36Sopenharmony_ci else 19662306a36Sopenharmony_ci usleep_range(osc->startup_usec, osc->startup_usec + 1); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci return 0; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic void clk_slow_rc_osc_unprepare(struct clk_hw *hw) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 20462306a36Sopenharmony_ci void __iomem *sckcr = osc->sckcr; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci writel(readl(sckcr) & ~osc->bits->cr_rcen, sckcr); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic int clk_slow_rc_osc_is_prepared(struct clk_hw *hw) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return !!(readl(osc->sckcr) & osc->bits->cr_rcen); 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic const struct clk_ops slow_rc_osc_ops = { 21762306a36Sopenharmony_ci .prepare = clk_slow_rc_osc_prepare, 21862306a36Sopenharmony_ci .unprepare = clk_slow_rc_osc_unprepare, 21962306a36Sopenharmony_ci .is_prepared = clk_slow_rc_osc_is_prepared, 22062306a36Sopenharmony_ci .recalc_rate = clk_slow_rc_osc_recalc_rate, 22162306a36Sopenharmony_ci .recalc_accuracy = clk_slow_rc_osc_recalc_accuracy, 22262306a36Sopenharmony_ci}; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic struct clk_hw * __init 22562306a36Sopenharmony_ciat91_clk_register_slow_rc_osc(void __iomem *sckcr, 22662306a36Sopenharmony_ci const char *name, 22762306a36Sopenharmony_ci unsigned long frequency, 22862306a36Sopenharmony_ci unsigned long accuracy, 22962306a36Sopenharmony_ci unsigned long startup, 23062306a36Sopenharmony_ci const struct clk_slow_bits *bits) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci struct clk_slow_rc_osc *osc; 23362306a36Sopenharmony_ci struct clk_hw *hw; 23462306a36Sopenharmony_ci struct clk_init_data init; 23562306a36Sopenharmony_ci int ret; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (!sckcr || !name) 23862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci osc = kzalloc(sizeof(*osc), GFP_KERNEL); 24162306a36Sopenharmony_ci if (!osc) 24262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci init.name = name; 24562306a36Sopenharmony_ci init.ops = &slow_rc_osc_ops; 24662306a36Sopenharmony_ci init.parent_names = NULL; 24762306a36Sopenharmony_ci init.num_parents = 0; 24862306a36Sopenharmony_ci init.flags = CLK_IGNORE_UNUSED; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci osc->hw.init = &init; 25162306a36Sopenharmony_ci osc->sckcr = sckcr; 25262306a36Sopenharmony_ci osc->bits = bits; 25362306a36Sopenharmony_ci osc->frequency = frequency; 25462306a36Sopenharmony_ci osc->accuracy = accuracy; 25562306a36Sopenharmony_ci osc->startup_usec = startup; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci hw = &osc->hw; 25862306a36Sopenharmony_ci ret = clk_hw_register(NULL, &osc->hw); 25962306a36Sopenharmony_ci if (ret) { 26062306a36Sopenharmony_ci kfree(osc); 26162306a36Sopenharmony_ci hw = ERR_PTR(ret); 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci return hw; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic void at91_clk_unregister_slow_rc_osc(struct clk_hw *hw) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci clk_hw_unregister(hw); 27262306a36Sopenharmony_ci kfree(osc); 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); 27862306a36Sopenharmony_ci void __iomem *sckcr = slowck->sckcr; 27962306a36Sopenharmony_ci u32 tmp; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (index > 1) 28262306a36Sopenharmony_ci return -EINVAL; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci tmp = readl(sckcr); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if ((!index && !(tmp & slowck->bits->cr_oscsel)) || 28762306a36Sopenharmony_ci (index && (tmp & slowck->bits->cr_oscsel))) 28862306a36Sopenharmony_ci return 0; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (index) 29162306a36Sopenharmony_ci tmp |= slowck->bits->cr_oscsel; 29262306a36Sopenharmony_ci else 29362306a36Sopenharmony_ci tmp &= ~slowck->bits->cr_oscsel; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci writel(tmp, sckcr); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (system_state < SYSTEM_RUNNING) 29862306a36Sopenharmony_ci udelay(SLOWCK_SW_TIME_USEC); 29962306a36Sopenharmony_ci else 30062306a36Sopenharmony_ci usleep_range(SLOWCK_SW_TIME_USEC, SLOWCK_SW_TIME_USEC + 1); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci return 0; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic u8 clk_sam9x5_slow_get_parent(struct clk_hw *hw) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci return !!(readl(slowck->sckcr) & slowck->bits->cr_oscsel); 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic const struct clk_ops sam9x5_slow_ops = { 31362306a36Sopenharmony_ci .determine_rate = clk_hw_determine_rate_no_reparent, 31462306a36Sopenharmony_ci .set_parent = clk_sam9x5_slow_set_parent, 31562306a36Sopenharmony_ci .get_parent = clk_sam9x5_slow_get_parent, 31662306a36Sopenharmony_ci}; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic struct clk_hw * __init 31962306a36Sopenharmony_ciat91_clk_register_sam9x5_slow(void __iomem *sckcr, 32062306a36Sopenharmony_ci const char *name, 32162306a36Sopenharmony_ci const struct clk_hw **parent_hws, 32262306a36Sopenharmony_ci int num_parents, 32362306a36Sopenharmony_ci const struct clk_slow_bits *bits) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct clk_sam9x5_slow *slowck; 32662306a36Sopenharmony_ci struct clk_hw *hw; 32762306a36Sopenharmony_ci struct clk_init_data init = {}; 32862306a36Sopenharmony_ci int ret; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (!sckcr || !name || !parent_hws || !num_parents) 33162306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci slowck = kzalloc(sizeof(*slowck), GFP_KERNEL); 33462306a36Sopenharmony_ci if (!slowck) 33562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci init.name = name; 33862306a36Sopenharmony_ci init.ops = &sam9x5_slow_ops; 33962306a36Sopenharmony_ci init.parent_hws = parent_hws; 34062306a36Sopenharmony_ci init.num_parents = num_parents; 34162306a36Sopenharmony_ci init.flags = 0; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci slowck->hw.init = &init; 34462306a36Sopenharmony_ci slowck->sckcr = sckcr; 34562306a36Sopenharmony_ci slowck->bits = bits; 34662306a36Sopenharmony_ci slowck->parent = !!(readl(sckcr) & slowck->bits->cr_oscsel); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci hw = &slowck->hw; 34962306a36Sopenharmony_ci ret = clk_hw_register(NULL, &slowck->hw); 35062306a36Sopenharmony_ci if (ret) { 35162306a36Sopenharmony_ci kfree(slowck); 35262306a36Sopenharmony_ci hw = ERR_PTR(ret); 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci return hw; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic void at91_clk_unregister_sam9x5_slow(struct clk_hw *hw) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci clk_hw_unregister(hw); 36362306a36Sopenharmony_ci kfree(slowck); 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic void __init at91sam9x5_sckc_register(struct device_node *np, 36762306a36Sopenharmony_ci unsigned int rc_osc_startup_us, 36862306a36Sopenharmony_ci const struct clk_slow_bits *bits) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci void __iomem *regbase = of_iomap(np, 0); 37162306a36Sopenharmony_ci struct device_node *child = NULL; 37262306a36Sopenharmony_ci const char *xtal_name; 37362306a36Sopenharmony_ci struct clk_hw *slow_rc, *slow_osc, *slowck; 37462306a36Sopenharmony_ci static struct clk_parent_data parent_data = { 37562306a36Sopenharmony_ci .name = "slow_xtal", 37662306a36Sopenharmony_ci }; 37762306a36Sopenharmony_ci const struct clk_hw *parent_hws[2]; 37862306a36Sopenharmony_ci bool bypass; 37962306a36Sopenharmony_ci int ret; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (!regbase) 38262306a36Sopenharmony_ci return; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci slow_rc = at91_clk_register_slow_rc_osc(regbase, "slow_rc_osc", 38562306a36Sopenharmony_ci 32768, 50000000, 38662306a36Sopenharmony_ci rc_osc_startup_us, bits); 38762306a36Sopenharmony_ci if (IS_ERR(slow_rc)) 38862306a36Sopenharmony_ci return; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci xtal_name = of_clk_get_parent_name(np, 0); 39162306a36Sopenharmony_ci if (!xtal_name) { 39262306a36Sopenharmony_ci /* DT backward compatibility */ 39362306a36Sopenharmony_ci child = of_get_compatible_child(np, "atmel,at91sam9x5-clk-slow-osc"); 39462306a36Sopenharmony_ci if (!child) 39562306a36Sopenharmony_ci goto unregister_slow_rc; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci xtal_name = of_clk_get_parent_name(child, 0); 39862306a36Sopenharmony_ci bypass = of_property_read_bool(child, "atmel,osc-bypass"); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci child = of_get_compatible_child(np, "atmel,at91sam9x5-clk-slow"); 40162306a36Sopenharmony_ci } else { 40262306a36Sopenharmony_ci bypass = of_property_read_bool(np, "atmel,osc-bypass"); 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (!xtal_name) 40662306a36Sopenharmony_ci goto unregister_slow_rc; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci parent_data.fw_name = xtal_name; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci slow_osc = at91_clk_register_slow_osc(regbase, "slow_osc", 41162306a36Sopenharmony_ci &parent_data, 1200000, bypass, bits); 41262306a36Sopenharmony_ci if (IS_ERR(slow_osc)) 41362306a36Sopenharmony_ci goto unregister_slow_rc; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci parent_hws[0] = slow_rc; 41662306a36Sopenharmony_ci parent_hws[1] = slow_osc; 41762306a36Sopenharmony_ci slowck = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_hws, 41862306a36Sopenharmony_ci 2, bits); 41962306a36Sopenharmony_ci if (IS_ERR(slowck)) 42062306a36Sopenharmony_ci goto unregister_slow_osc; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* DT backward compatibility */ 42362306a36Sopenharmony_ci if (child) 42462306a36Sopenharmony_ci ret = of_clk_add_hw_provider(child, of_clk_hw_simple_get, 42562306a36Sopenharmony_ci slowck); 42662306a36Sopenharmony_ci else 42762306a36Sopenharmony_ci ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, slowck); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (WARN_ON(ret)) 43062306a36Sopenharmony_ci goto unregister_slowck; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci return; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ciunregister_slowck: 43562306a36Sopenharmony_ci at91_clk_unregister_sam9x5_slow(slowck); 43662306a36Sopenharmony_ciunregister_slow_osc: 43762306a36Sopenharmony_ci at91_clk_unregister_slow_osc(slow_osc); 43862306a36Sopenharmony_ciunregister_slow_rc: 43962306a36Sopenharmony_ci at91_clk_unregister_slow_rc_osc(slow_rc); 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic const struct clk_slow_bits at91sam9x5_bits = { 44362306a36Sopenharmony_ci .cr_rcen = BIT(0), 44462306a36Sopenharmony_ci .cr_osc32en = BIT(1), 44562306a36Sopenharmony_ci .cr_osc32byp = BIT(2), 44662306a36Sopenharmony_ci .cr_oscsel = BIT(3), 44762306a36Sopenharmony_ci}; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic void __init of_at91sam9x5_sckc_setup(struct device_node *np) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci at91sam9x5_sckc_register(np, 75, &at91sam9x5_bits); 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ciCLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc", 45462306a36Sopenharmony_ci of_at91sam9x5_sckc_setup); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic void __init of_sama5d3_sckc_setup(struct device_node *np) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci at91sam9x5_sckc_register(np, 500, &at91sam9x5_bits); 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ciCLK_OF_DECLARE(sama5d3_clk_sckc, "atmel,sama5d3-sckc", 46162306a36Sopenharmony_ci of_sama5d3_sckc_setup); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic const struct clk_slow_bits at91sam9x60_bits = { 46462306a36Sopenharmony_ci .cr_osc32en = BIT(1), 46562306a36Sopenharmony_ci .cr_osc32byp = BIT(2), 46662306a36Sopenharmony_ci .cr_oscsel = BIT(24), 46762306a36Sopenharmony_ci}; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic void __init of_sam9x60_sckc_setup(struct device_node *np) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci void __iomem *regbase = of_iomap(np, 0); 47262306a36Sopenharmony_ci struct clk_hw_onecell_data *clk_data; 47362306a36Sopenharmony_ci struct clk_hw *slow_rc, *slow_osc; 47462306a36Sopenharmony_ci const char *xtal_name; 47562306a36Sopenharmony_ci const struct clk_hw *parent_hws[2]; 47662306a36Sopenharmony_ci static struct clk_parent_data parent_data = { 47762306a36Sopenharmony_ci .name = "slow_xtal", 47862306a36Sopenharmony_ci }; 47962306a36Sopenharmony_ci bool bypass; 48062306a36Sopenharmony_ci int ret; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (!regbase) 48362306a36Sopenharmony_ci return; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL, "slow_rc_osc", 48662306a36Sopenharmony_ci NULL, 0, 32768, 48762306a36Sopenharmony_ci 93750000); 48862306a36Sopenharmony_ci if (IS_ERR(slow_rc)) 48962306a36Sopenharmony_ci return; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci xtal_name = of_clk_get_parent_name(np, 0); 49262306a36Sopenharmony_ci if (!xtal_name) 49362306a36Sopenharmony_ci goto unregister_slow_rc; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci parent_data.fw_name = xtal_name; 49662306a36Sopenharmony_ci bypass = of_property_read_bool(np, "atmel,osc-bypass"); 49762306a36Sopenharmony_ci slow_osc = at91_clk_register_slow_osc(regbase, "slow_osc", 49862306a36Sopenharmony_ci &parent_data, 5000000, bypass, 49962306a36Sopenharmony_ci &at91sam9x60_bits); 50062306a36Sopenharmony_ci if (IS_ERR(slow_osc)) 50162306a36Sopenharmony_ci goto unregister_slow_rc; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci clk_data = kzalloc(struct_size(clk_data, hws, 2), GFP_KERNEL); 50462306a36Sopenharmony_ci if (!clk_data) 50562306a36Sopenharmony_ci goto unregister_slow_osc; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* MD_SLCK and TD_SLCK. */ 50862306a36Sopenharmony_ci clk_data->num = 2; 50962306a36Sopenharmony_ci clk_data->hws[0] = clk_hw_register_fixed_rate_parent_hw(NULL, "md_slck", 51062306a36Sopenharmony_ci slow_rc, 51162306a36Sopenharmony_ci 0, 32768); 51262306a36Sopenharmony_ci if (IS_ERR(clk_data->hws[0])) 51362306a36Sopenharmony_ci goto clk_data_free; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci parent_hws[0] = slow_rc; 51662306a36Sopenharmony_ci parent_hws[1] = slow_osc; 51762306a36Sopenharmony_ci clk_data->hws[1] = at91_clk_register_sam9x5_slow(regbase, "td_slck", 51862306a36Sopenharmony_ci parent_hws, 2, 51962306a36Sopenharmony_ci &at91sam9x60_bits); 52062306a36Sopenharmony_ci if (IS_ERR(clk_data->hws[1])) 52162306a36Sopenharmony_ci goto unregister_md_slck; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data); 52462306a36Sopenharmony_ci if (WARN_ON(ret)) 52562306a36Sopenharmony_ci goto unregister_td_slck; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci return; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ciunregister_td_slck: 53062306a36Sopenharmony_ci at91_clk_unregister_sam9x5_slow(clk_data->hws[1]); 53162306a36Sopenharmony_ciunregister_md_slck: 53262306a36Sopenharmony_ci clk_hw_unregister(clk_data->hws[0]); 53362306a36Sopenharmony_ciclk_data_free: 53462306a36Sopenharmony_ci kfree(clk_data); 53562306a36Sopenharmony_ciunregister_slow_osc: 53662306a36Sopenharmony_ci at91_clk_unregister_slow_osc(slow_osc); 53762306a36Sopenharmony_ciunregister_slow_rc: 53862306a36Sopenharmony_ci clk_hw_unregister(slow_rc); 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ciCLK_OF_DECLARE(sam9x60_clk_sckc, "microchip,sam9x60-sckc", 54162306a36Sopenharmony_ci of_sam9x60_sckc_setup); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cistatic int clk_sama5d4_slow_osc_prepare(struct clk_hw *hw) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (osc->prepared) 54862306a36Sopenharmony_ci return 0; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* 55162306a36Sopenharmony_ci * Assume that if it has already been selected (for example by the 55262306a36Sopenharmony_ci * bootloader), enough time has already passed. 55362306a36Sopenharmony_ci */ 55462306a36Sopenharmony_ci if ((readl(osc->sckcr) & osc->bits->cr_oscsel)) { 55562306a36Sopenharmony_ci osc->prepared = true; 55662306a36Sopenharmony_ci return 0; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (system_state < SYSTEM_RUNNING) 56062306a36Sopenharmony_ci udelay(osc->startup_usec); 56162306a36Sopenharmony_ci else 56262306a36Sopenharmony_ci usleep_range(osc->startup_usec, osc->startup_usec + 1); 56362306a36Sopenharmony_ci osc->prepared = true; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci return 0; 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic int clk_sama5d4_slow_osc_is_prepared(struct clk_hw *hw) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci return osc->prepared; 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_cistatic const struct clk_ops sama5d4_slow_osc_ops = { 57662306a36Sopenharmony_ci .prepare = clk_sama5d4_slow_osc_prepare, 57762306a36Sopenharmony_ci .is_prepared = clk_sama5d4_slow_osc_is_prepared, 57862306a36Sopenharmony_ci}; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_cistatic const struct clk_slow_bits at91sama5d4_bits = { 58162306a36Sopenharmony_ci .cr_oscsel = BIT(3), 58262306a36Sopenharmony_ci}; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cistatic void __init of_sama5d4_sckc_setup(struct device_node *np) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci void __iomem *regbase = of_iomap(np, 0); 58762306a36Sopenharmony_ci struct clk_hw *slow_rc, *slowck; 58862306a36Sopenharmony_ci struct clk_sama5d4_slow_osc *osc; 58962306a36Sopenharmony_ci struct clk_init_data init = {}; 59062306a36Sopenharmony_ci const char *xtal_name; 59162306a36Sopenharmony_ci const struct clk_hw *parent_hws[2]; 59262306a36Sopenharmony_ci static struct clk_parent_data parent_data = { 59362306a36Sopenharmony_ci .name = "slow_xtal", 59462306a36Sopenharmony_ci }; 59562306a36Sopenharmony_ci int ret; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (!regbase) 59862306a36Sopenharmony_ci return; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL, 60162306a36Sopenharmony_ci "slow_rc_osc", 60262306a36Sopenharmony_ci NULL, 0, 32768, 60362306a36Sopenharmony_ci 250000000); 60462306a36Sopenharmony_ci if (IS_ERR(slow_rc)) 60562306a36Sopenharmony_ci return; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci xtal_name = of_clk_get_parent_name(np, 0); 60862306a36Sopenharmony_ci if (!xtal_name) 60962306a36Sopenharmony_ci goto unregister_slow_rc; 61062306a36Sopenharmony_ci parent_data.fw_name = xtal_name; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci osc = kzalloc(sizeof(*osc), GFP_KERNEL); 61362306a36Sopenharmony_ci if (!osc) 61462306a36Sopenharmony_ci goto unregister_slow_rc; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci init.name = "slow_osc"; 61762306a36Sopenharmony_ci init.ops = &sama5d4_slow_osc_ops; 61862306a36Sopenharmony_ci init.parent_data = &parent_data; 61962306a36Sopenharmony_ci init.num_parents = 1; 62062306a36Sopenharmony_ci init.flags = CLK_IGNORE_UNUSED; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci osc->hw.init = &init; 62362306a36Sopenharmony_ci osc->sckcr = regbase; 62462306a36Sopenharmony_ci osc->startup_usec = 1200000; 62562306a36Sopenharmony_ci osc->bits = &at91sama5d4_bits; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci ret = clk_hw_register(NULL, &osc->hw); 62862306a36Sopenharmony_ci if (ret) 62962306a36Sopenharmony_ci goto free_slow_osc_data; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci parent_hws[0] = slow_rc; 63262306a36Sopenharmony_ci parent_hws[1] = &osc->hw; 63362306a36Sopenharmony_ci slowck = at91_clk_register_sam9x5_slow(regbase, "slowck", 63462306a36Sopenharmony_ci parent_hws, 2, 63562306a36Sopenharmony_ci &at91sama5d4_bits); 63662306a36Sopenharmony_ci if (IS_ERR(slowck)) 63762306a36Sopenharmony_ci goto unregister_slow_osc; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, slowck); 64062306a36Sopenharmony_ci if (WARN_ON(ret)) 64162306a36Sopenharmony_ci goto unregister_slowck; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci return; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ciunregister_slowck: 64662306a36Sopenharmony_ci at91_clk_unregister_sam9x5_slow(slowck); 64762306a36Sopenharmony_ciunregister_slow_osc: 64862306a36Sopenharmony_ci clk_hw_unregister(&osc->hw); 64962306a36Sopenharmony_cifree_slow_osc_data: 65062306a36Sopenharmony_ci kfree(osc); 65162306a36Sopenharmony_ciunregister_slow_rc: 65262306a36Sopenharmony_ci clk_hw_unregister(slow_rc); 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ciCLK_OF_DECLARE(sama5d4_clk_sckc, "atmel,sama5d4-sckc", 65562306a36Sopenharmony_ci of_sama5d4_sckc_setup); 656