162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Nomadik clock implementation 462306a36Sopenharmony_ci * Copyright (C) 2013 ST-Ericsson AB 562306a36Sopenharmony_ci * Author: Linus Walleij <linus.walleij@linaro.org> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define pr_fmt(fmt) "Nomadik SRC clocks: " fmt 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/bitops.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/err.h> 1362306a36Sopenharmony_ci#include <linux/io.h> 1462306a36Sopenharmony_ci#include <linux/clk-provider.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/of_address.h> 1762306a36Sopenharmony_ci#include <linux/debugfs.h> 1862306a36Sopenharmony_ci#include <linux/seq_file.h> 1962306a36Sopenharmony_ci#include <linux/spinlock.h> 2062306a36Sopenharmony_ci#include <linux/reboot.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* 2362306a36Sopenharmony_ci * The Nomadik clock tree is described in the STN8815A12 DB V4.2 2462306a36Sopenharmony_ci * reference manual for the chip, page 94 ff. 2562306a36Sopenharmony_ci * Clock IDs are in the STn8815 Reference Manual table 3, page 27. 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define SRC_CR 0x00U 2962306a36Sopenharmony_ci#define SRC_CR_T0_ENSEL BIT(15) 3062306a36Sopenharmony_ci#define SRC_CR_T1_ENSEL BIT(17) 3162306a36Sopenharmony_ci#define SRC_CR_T2_ENSEL BIT(19) 3262306a36Sopenharmony_ci#define SRC_CR_T3_ENSEL BIT(21) 3362306a36Sopenharmony_ci#define SRC_CR_T4_ENSEL BIT(23) 3462306a36Sopenharmony_ci#define SRC_CR_T5_ENSEL BIT(25) 3562306a36Sopenharmony_ci#define SRC_CR_T6_ENSEL BIT(27) 3662306a36Sopenharmony_ci#define SRC_CR_T7_ENSEL BIT(29) 3762306a36Sopenharmony_ci#define SRC_XTALCR 0x0CU 3862306a36Sopenharmony_ci#define SRC_XTALCR_XTALTIMEN BIT(20) 3962306a36Sopenharmony_ci#define SRC_XTALCR_SXTALDIS BIT(19) 4062306a36Sopenharmony_ci#define SRC_XTALCR_MXTALSTAT BIT(2) 4162306a36Sopenharmony_ci#define SRC_XTALCR_MXTALEN BIT(1) 4262306a36Sopenharmony_ci#define SRC_XTALCR_MXTALOVER BIT(0) 4362306a36Sopenharmony_ci#define SRC_PLLCR 0x10U 4462306a36Sopenharmony_ci#define SRC_PLLCR_PLLTIMEN BIT(29) 4562306a36Sopenharmony_ci#define SRC_PLLCR_PLL2EN BIT(28) 4662306a36Sopenharmony_ci#define SRC_PLLCR_PLL1STAT BIT(2) 4762306a36Sopenharmony_ci#define SRC_PLLCR_PLL1EN BIT(1) 4862306a36Sopenharmony_ci#define SRC_PLLCR_PLL1OVER BIT(0) 4962306a36Sopenharmony_ci#define SRC_PLLFR 0x14U 5062306a36Sopenharmony_ci#define SRC_PCKEN0 0x24U 5162306a36Sopenharmony_ci#define SRC_PCKDIS0 0x28U 5262306a36Sopenharmony_ci#define SRC_PCKENSR0 0x2CU 5362306a36Sopenharmony_ci#define SRC_PCKSR0 0x30U 5462306a36Sopenharmony_ci#define SRC_PCKEN1 0x34U 5562306a36Sopenharmony_ci#define SRC_PCKDIS1 0x38U 5662306a36Sopenharmony_ci#define SRC_PCKENSR1 0x3CU 5762306a36Sopenharmony_ci#define SRC_PCKSR1 0x40U 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* Lock protecting the SRC_CR register */ 6062306a36Sopenharmony_cistatic DEFINE_SPINLOCK(src_lock); 6162306a36Sopenharmony_ci/* Base address of the SRC */ 6262306a36Sopenharmony_cistatic void __iomem *src_base; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic int nomadik_clk_reboot_handler(struct notifier_block *this, 6562306a36Sopenharmony_ci unsigned long code, 6662306a36Sopenharmony_ci void *unused) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci u32 val; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* The main chrystal need to be enabled for reboot to work */ 7162306a36Sopenharmony_ci val = readl(src_base + SRC_XTALCR); 7262306a36Sopenharmony_ci val &= ~SRC_XTALCR_MXTALOVER; 7362306a36Sopenharmony_ci val |= SRC_XTALCR_MXTALEN; 7462306a36Sopenharmony_ci pr_crit("force-enabling MXTALO\n"); 7562306a36Sopenharmony_ci writel(val, src_base + SRC_XTALCR); 7662306a36Sopenharmony_ci return NOTIFY_OK; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic struct notifier_block nomadik_clk_reboot_notifier = { 8062306a36Sopenharmony_ci .notifier_call = nomadik_clk_reboot_handler, 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic const struct of_device_id nomadik_src_match[] __initconst = { 8462306a36Sopenharmony_ci { .compatible = "stericsson,nomadik-src" }, 8562306a36Sopenharmony_ci { /* sentinel */ } 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic void __init nomadik_src_init(void) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct device_node *np; 9162306a36Sopenharmony_ci u32 val; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci np = of_find_matching_node(NULL, nomadik_src_match); 9462306a36Sopenharmony_ci if (!np) { 9562306a36Sopenharmony_ci pr_crit("no matching node for SRC, aborting clock init\n"); 9662306a36Sopenharmony_ci return; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci src_base = of_iomap(np, 0); 9962306a36Sopenharmony_ci if (!src_base) { 10062306a36Sopenharmony_ci pr_err("%s: must have src parent node with REGS (%pOFn)\n", 10162306a36Sopenharmony_ci __func__, np); 10262306a36Sopenharmony_ci goto out_put; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* Set all timers to use the 2.4 MHz TIMCLK */ 10662306a36Sopenharmony_ci val = readl(src_base + SRC_CR); 10762306a36Sopenharmony_ci val |= SRC_CR_T0_ENSEL; 10862306a36Sopenharmony_ci val |= SRC_CR_T1_ENSEL; 10962306a36Sopenharmony_ci val |= SRC_CR_T2_ENSEL; 11062306a36Sopenharmony_ci val |= SRC_CR_T3_ENSEL; 11162306a36Sopenharmony_ci val |= SRC_CR_T4_ENSEL; 11262306a36Sopenharmony_ci val |= SRC_CR_T5_ENSEL; 11362306a36Sopenharmony_ci val |= SRC_CR_T6_ENSEL; 11462306a36Sopenharmony_ci val |= SRC_CR_T7_ENSEL; 11562306a36Sopenharmony_ci writel(val, src_base + SRC_CR); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci val = readl(src_base + SRC_XTALCR); 11862306a36Sopenharmony_ci pr_info("SXTALO is %s\n", 11962306a36Sopenharmony_ci (val & SRC_XTALCR_SXTALDIS) ? "disabled" : "enabled"); 12062306a36Sopenharmony_ci pr_info("MXTAL is %s\n", 12162306a36Sopenharmony_ci (val & SRC_XTALCR_MXTALSTAT) ? "enabled" : "disabled"); 12262306a36Sopenharmony_ci if (of_property_read_bool(np, "disable-sxtalo")) { 12362306a36Sopenharmony_ci /* The machine uses an external oscillator circuit */ 12462306a36Sopenharmony_ci val |= SRC_XTALCR_SXTALDIS; 12562306a36Sopenharmony_ci pr_info("disabling SXTALO\n"); 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci if (of_property_read_bool(np, "disable-mxtalo")) { 12862306a36Sopenharmony_ci /* Disable this too: also run by external oscillator */ 12962306a36Sopenharmony_ci val |= SRC_XTALCR_MXTALOVER; 13062306a36Sopenharmony_ci val &= ~SRC_XTALCR_MXTALEN; 13162306a36Sopenharmony_ci pr_info("disabling MXTALO\n"); 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci writel(val, src_base + SRC_XTALCR); 13462306a36Sopenharmony_ci register_reboot_notifier(&nomadik_clk_reboot_notifier); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ciout_put: 13762306a36Sopenharmony_ci of_node_put(np); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/** 14162306a36Sopenharmony_ci * struct clk_pll - Nomadik PLL clock 14262306a36Sopenharmony_ci * @hw: corresponding clock hardware entry 14362306a36Sopenharmony_ci * @id: PLL instance: 1 or 2 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_cistruct clk_pll { 14662306a36Sopenharmony_ci struct clk_hw hw; 14762306a36Sopenharmony_ci int id; 14862306a36Sopenharmony_ci}; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci/** 15162306a36Sopenharmony_ci * struct clk_src - Nomadik src clock 15262306a36Sopenharmony_ci * @hw: corresponding clock hardware entry 15362306a36Sopenharmony_ci * @id: the clock ID 15462306a36Sopenharmony_ci * @group1: true if the clock is in group1, else it is in group0 15562306a36Sopenharmony_ci * @clkbit: bit 0...31 corresponding to the clock in each clock register 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_cistruct clk_src { 15862306a36Sopenharmony_ci struct clk_hw hw; 15962306a36Sopenharmony_ci int id; 16062306a36Sopenharmony_ci bool group1; 16162306a36Sopenharmony_ci u32 clkbit; 16262306a36Sopenharmony_ci}; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci#define to_pll(_hw) container_of(_hw, struct clk_pll, hw) 16562306a36Sopenharmony_ci#define to_src(_hw) container_of(_hw, struct clk_src, hw) 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic int pll_clk_enable(struct clk_hw *hw) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct clk_pll *pll = to_pll(hw); 17062306a36Sopenharmony_ci u32 val; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci spin_lock(&src_lock); 17362306a36Sopenharmony_ci val = readl(src_base + SRC_PLLCR); 17462306a36Sopenharmony_ci if (pll->id == 1) { 17562306a36Sopenharmony_ci if (val & SRC_PLLCR_PLL1OVER) { 17662306a36Sopenharmony_ci val |= SRC_PLLCR_PLL1EN; 17762306a36Sopenharmony_ci writel(val, src_base + SRC_PLLCR); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci } else if (pll->id == 2) { 18062306a36Sopenharmony_ci val |= SRC_PLLCR_PLL2EN; 18162306a36Sopenharmony_ci writel(val, src_base + SRC_PLLCR); 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci spin_unlock(&src_lock); 18462306a36Sopenharmony_ci return 0; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic void pll_clk_disable(struct clk_hw *hw) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci struct clk_pll *pll = to_pll(hw); 19062306a36Sopenharmony_ci u32 val; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci spin_lock(&src_lock); 19362306a36Sopenharmony_ci val = readl(src_base + SRC_PLLCR); 19462306a36Sopenharmony_ci if (pll->id == 1) { 19562306a36Sopenharmony_ci if (val & SRC_PLLCR_PLL1OVER) { 19662306a36Sopenharmony_ci val &= ~SRC_PLLCR_PLL1EN; 19762306a36Sopenharmony_ci writel(val, src_base + SRC_PLLCR); 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci } else if (pll->id == 2) { 20062306a36Sopenharmony_ci val &= ~SRC_PLLCR_PLL2EN; 20162306a36Sopenharmony_ci writel(val, src_base + SRC_PLLCR); 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci spin_unlock(&src_lock); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic int pll_clk_is_enabled(struct clk_hw *hw) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct clk_pll *pll = to_pll(hw); 20962306a36Sopenharmony_ci u32 val; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci val = readl(src_base + SRC_PLLCR); 21262306a36Sopenharmony_ci if (pll->id == 1) { 21362306a36Sopenharmony_ci if (val & SRC_PLLCR_PLL1OVER) 21462306a36Sopenharmony_ci return !!(val & SRC_PLLCR_PLL1EN); 21562306a36Sopenharmony_ci } else if (pll->id == 2) { 21662306a36Sopenharmony_ci return !!(val & SRC_PLLCR_PLL2EN); 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci return 1; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic unsigned long pll_clk_recalc_rate(struct clk_hw *hw, 22262306a36Sopenharmony_ci unsigned long parent_rate) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct clk_pll *pll = to_pll(hw); 22562306a36Sopenharmony_ci u32 val; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci val = readl(src_base + SRC_PLLFR); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (pll->id == 1) { 23062306a36Sopenharmony_ci u8 mul; 23162306a36Sopenharmony_ci u8 div; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci mul = (val >> 8) & 0x3FU; 23462306a36Sopenharmony_ci mul += 2; 23562306a36Sopenharmony_ci div = val & 0x07U; 23662306a36Sopenharmony_ci return (parent_rate * mul) >> div; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (pll->id == 2) { 24062306a36Sopenharmony_ci u8 mul; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci mul = (val >> 24) & 0x3FU; 24362306a36Sopenharmony_ci mul += 2; 24462306a36Sopenharmony_ci return (parent_rate * mul); 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* Unknown PLL */ 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic const struct clk_ops pll_clk_ops = { 25362306a36Sopenharmony_ci .enable = pll_clk_enable, 25462306a36Sopenharmony_ci .disable = pll_clk_disable, 25562306a36Sopenharmony_ci .is_enabled = pll_clk_is_enabled, 25662306a36Sopenharmony_ci .recalc_rate = pll_clk_recalc_rate, 25762306a36Sopenharmony_ci}; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic struct clk_hw * __init 26062306a36Sopenharmony_cipll_clk_register(struct device *dev, const char *name, 26162306a36Sopenharmony_ci const char *parent_name, u32 id) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci int ret; 26462306a36Sopenharmony_ci struct clk_pll *pll; 26562306a36Sopenharmony_ci struct clk_init_data init; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (id != 1 && id != 2) { 26862306a36Sopenharmony_ci pr_err("%s: the Nomadik has only PLL 1 & 2\n", __func__); 26962306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci pll = kzalloc(sizeof(*pll), GFP_KERNEL); 27362306a36Sopenharmony_ci if (!pll) 27462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci init.name = name; 27762306a36Sopenharmony_ci init.ops = &pll_clk_ops; 27862306a36Sopenharmony_ci init.parent_names = (parent_name ? &parent_name : NULL); 27962306a36Sopenharmony_ci init.num_parents = (parent_name ? 1 : 0); 28062306a36Sopenharmony_ci pll->hw.init = &init; 28162306a36Sopenharmony_ci pll->id = id; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci pr_debug("register PLL1 clock \"%s\"\n", name); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci ret = clk_hw_register(dev, &pll->hw); 28662306a36Sopenharmony_ci if (ret) { 28762306a36Sopenharmony_ci kfree(pll); 28862306a36Sopenharmony_ci return ERR_PTR(ret); 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci return &pll->hw; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci/* 29562306a36Sopenharmony_ci * The Nomadik SRC clocks are gated, but not in the sense that 29662306a36Sopenharmony_ci * you read-modify-write a register. Instead there are separate 29762306a36Sopenharmony_ci * clock enable and clock disable registers. Writing a '1' bit in 29862306a36Sopenharmony_ci * the enable register for a certain clock ungates that clock without 29962306a36Sopenharmony_ci * affecting the other clocks. The disable register works the opposite 30062306a36Sopenharmony_ci * way. 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic int src_clk_enable(struct clk_hw *hw) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci struct clk_src *sclk = to_src(hw); 30662306a36Sopenharmony_ci u32 enreg = sclk->group1 ? SRC_PCKEN1 : SRC_PCKEN0; 30762306a36Sopenharmony_ci u32 sreg = sclk->group1 ? SRC_PCKSR1 : SRC_PCKSR0; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci writel(sclk->clkbit, src_base + enreg); 31062306a36Sopenharmony_ci /* spin until enabled */ 31162306a36Sopenharmony_ci while (!(readl(src_base + sreg) & sclk->clkbit)) 31262306a36Sopenharmony_ci cpu_relax(); 31362306a36Sopenharmony_ci return 0; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic void src_clk_disable(struct clk_hw *hw) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci struct clk_src *sclk = to_src(hw); 31962306a36Sopenharmony_ci u32 disreg = sclk->group1 ? SRC_PCKDIS1 : SRC_PCKDIS0; 32062306a36Sopenharmony_ci u32 sreg = sclk->group1 ? SRC_PCKSR1 : SRC_PCKSR0; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci writel(sclk->clkbit, src_base + disreg); 32362306a36Sopenharmony_ci /* spin until disabled */ 32462306a36Sopenharmony_ci while (readl(src_base + sreg) & sclk->clkbit) 32562306a36Sopenharmony_ci cpu_relax(); 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic int src_clk_is_enabled(struct clk_hw *hw) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci struct clk_src *sclk = to_src(hw); 33162306a36Sopenharmony_ci u32 sreg = sclk->group1 ? SRC_PCKSR1 : SRC_PCKSR0; 33262306a36Sopenharmony_ci u32 val = readl(src_base + sreg); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci return !!(val & sclk->clkbit); 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic unsigned long 33862306a36Sopenharmony_cisrc_clk_recalc_rate(struct clk_hw *hw, 33962306a36Sopenharmony_ci unsigned long parent_rate) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci return parent_rate; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic const struct clk_ops src_clk_ops = { 34562306a36Sopenharmony_ci .enable = src_clk_enable, 34662306a36Sopenharmony_ci .disable = src_clk_disable, 34762306a36Sopenharmony_ci .is_enabled = src_clk_is_enabled, 34862306a36Sopenharmony_ci .recalc_rate = src_clk_recalc_rate, 34962306a36Sopenharmony_ci}; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic struct clk_hw * __init 35262306a36Sopenharmony_cisrc_clk_register(struct device *dev, const char *name, 35362306a36Sopenharmony_ci const char *parent_name, u8 id) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci int ret; 35662306a36Sopenharmony_ci struct clk_src *sclk; 35762306a36Sopenharmony_ci struct clk_init_data init; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci sclk = kzalloc(sizeof(*sclk), GFP_KERNEL); 36062306a36Sopenharmony_ci if (!sclk) 36162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci init.name = name; 36462306a36Sopenharmony_ci init.ops = &src_clk_ops; 36562306a36Sopenharmony_ci /* Do not force-disable the static SDRAM controller */ 36662306a36Sopenharmony_ci if (id == 2) 36762306a36Sopenharmony_ci init.flags = CLK_IGNORE_UNUSED; 36862306a36Sopenharmony_ci else 36962306a36Sopenharmony_ci init.flags = 0; 37062306a36Sopenharmony_ci init.parent_names = (parent_name ? &parent_name : NULL); 37162306a36Sopenharmony_ci init.num_parents = (parent_name ? 1 : 0); 37262306a36Sopenharmony_ci sclk->hw.init = &init; 37362306a36Sopenharmony_ci sclk->id = id; 37462306a36Sopenharmony_ci sclk->group1 = (id > 31); 37562306a36Sopenharmony_ci sclk->clkbit = BIT(id & 0x1f); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci pr_debug("register clock \"%s\" ID: %d group: %d bits: %08x\n", 37862306a36Sopenharmony_ci name, id, sclk->group1, sclk->clkbit); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci ret = clk_hw_register(dev, &sclk->hw); 38162306a36Sopenharmony_ci if (ret) { 38262306a36Sopenharmony_ci kfree(sclk); 38362306a36Sopenharmony_ci return ERR_PTR(ret); 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci return &sclk->hw; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic u32 src_pcksr0_boot; 39262306a36Sopenharmony_cistatic u32 src_pcksr1_boot; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic const char * const src_clk_names[] = { 39562306a36Sopenharmony_ci "HCLKDMA0 ", 39662306a36Sopenharmony_ci "HCLKSMC ", 39762306a36Sopenharmony_ci "HCLKSDRAM ", 39862306a36Sopenharmony_ci "HCLKDMA1 ", 39962306a36Sopenharmony_ci "HCLKCLCD ", 40062306a36Sopenharmony_ci "PCLKIRDA ", 40162306a36Sopenharmony_ci "PCLKSSP ", 40262306a36Sopenharmony_ci "PCLKUART0 ", 40362306a36Sopenharmony_ci "PCLKSDI ", 40462306a36Sopenharmony_ci "PCLKI2C0 ", 40562306a36Sopenharmony_ci "PCLKI2C1 ", 40662306a36Sopenharmony_ci "PCLKUART1 ", 40762306a36Sopenharmony_ci "PCLMSP0 ", 40862306a36Sopenharmony_ci "HCLKUSB ", 40962306a36Sopenharmony_ci "HCLKDIF ", 41062306a36Sopenharmony_ci "HCLKSAA ", 41162306a36Sopenharmony_ci "HCLKSVA ", 41262306a36Sopenharmony_ci "PCLKHSI ", 41362306a36Sopenharmony_ci "PCLKXTI ", 41462306a36Sopenharmony_ci "PCLKUART2 ", 41562306a36Sopenharmony_ci "PCLKMSP1 ", 41662306a36Sopenharmony_ci "PCLKMSP2 ", 41762306a36Sopenharmony_ci "PCLKOWM ", 41862306a36Sopenharmony_ci "HCLKHPI ", 41962306a36Sopenharmony_ci "PCLKSKE ", 42062306a36Sopenharmony_ci "PCLKHSEM ", 42162306a36Sopenharmony_ci "HCLK3D ", 42262306a36Sopenharmony_ci "HCLKHASH ", 42362306a36Sopenharmony_ci "HCLKCRYP ", 42462306a36Sopenharmony_ci "PCLKMSHC ", 42562306a36Sopenharmony_ci "HCLKUSBM ", 42662306a36Sopenharmony_ci "HCLKRNG ", 42762306a36Sopenharmony_ci "RESERVED ", 42862306a36Sopenharmony_ci "RESERVED ", 42962306a36Sopenharmony_ci "RESERVED ", 43062306a36Sopenharmony_ci "RESERVED ", 43162306a36Sopenharmony_ci "CLDCLK ", 43262306a36Sopenharmony_ci "IRDACLK ", 43362306a36Sopenharmony_ci "SSPICLK ", 43462306a36Sopenharmony_ci "UART0CLK ", 43562306a36Sopenharmony_ci "SDICLK ", 43662306a36Sopenharmony_ci "I2C0CLK ", 43762306a36Sopenharmony_ci "I2C1CLK ", 43862306a36Sopenharmony_ci "UART1CLK ", 43962306a36Sopenharmony_ci "MSPCLK0 ", 44062306a36Sopenharmony_ci "USBCLK ", 44162306a36Sopenharmony_ci "DIFCLK ", 44262306a36Sopenharmony_ci "IPI2CCLK ", 44362306a36Sopenharmony_ci "IPBMCCLK ", 44462306a36Sopenharmony_ci "HSICLKRX ", 44562306a36Sopenharmony_ci "HSICLKTX ", 44662306a36Sopenharmony_ci "UART2CLK ", 44762306a36Sopenharmony_ci "MSPCLK1 ", 44862306a36Sopenharmony_ci "MSPCLK2 ", 44962306a36Sopenharmony_ci "OWMCLK ", 45062306a36Sopenharmony_ci "RESERVED ", 45162306a36Sopenharmony_ci "SKECLK ", 45262306a36Sopenharmony_ci "RESERVED ", 45362306a36Sopenharmony_ci "3DCLK ", 45462306a36Sopenharmony_ci "PCLKMSP3 ", 45562306a36Sopenharmony_ci "MSPCLK3 ", 45662306a36Sopenharmony_ci "MSHCCLK ", 45762306a36Sopenharmony_ci "USBMCLK ", 45862306a36Sopenharmony_ci "RNGCCLK ", 45962306a36Sopenharmony_ci}; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic int nomadik_src_clk_debugfs_show(struct seq_file *s, void *what) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci int i; 46462306a36Sopenharmony_ci u32 src_pcksr0 = readl(src_base + SRC_PCKSR0); 46562306a36Sopenharmony_ci u32 src_pcksr1 = readl(src_base + SRC_PCKSR1); 46662306a36Sopenharmony_ci u32 src_pckensr0 = readl(src_base + SRC_PCKENSR0); 46762306a36Sopenharmony_ci u32 src_pckensr1 = readl(src_base + SRC_PCKENSR1); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci seq_puts(s, "Clock: Boot: Now: Request: ASKED:\n"); 47062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(src_clk_names); i++) { 47162306a36Sopenharmony_ci u32 pcksrb = (i < 0x20) ? src_pcksr0_boot : src_pcksr1_boot; 47262306a36Sopenharmony_ci u32 pcksr = (i < 0x20) ? src_pcksr0 : src_pcksr1; 47362306a36Sopenharmony_ci u32 pckreq = (i < 0x20) ? src_pckensr0 : src_pckensr1; 47462306a36Sopenharmony_ci u32 mask = BIT(i & 0x1f); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci seq_printf(s, "%s %s %s %s\n", 47762306a36Sopenharmony_ci src_clk_names[i], 47862306a36Sopenharmony_ci (pcksrb & mask) ? "on " : "off", 47962306a36Sopenharmony_ci (pcksr & mask) ? "on " : "off", 48062306a36Sopenharmony_ci (pckreq & mask) ? "on " : "off"); 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci return 0; 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(nomadik_src_clk_debugfs); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistatic int __init nomadik_src_clk_init_debugfs(void) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci /* Vital for multiplatform */ 49062306a36Sopenharmony_ci if (!src_base) 49162306a36Sopenharmony_ci return -ENODEV; 49262306a36Sopenharmony_ci src_pcksr0_boot = readl(src_base + SRC_PCKSR0); 49362306a36Sopenharmony_ci src_pcksr1_boot = readl(src_base + SRC_PCKSR1); 49462306a36Sopenharmony_ci debugfs_create_file("nomadik-src-clk", S_IFREG | S_IRUGO, 49562306a36Sopenharmony_ci NULL, NULL, &nomadik_src_clk_debugfs_fops); 49662306a36Sopenharmony_ci return 0; 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_cidevice_initcall(nomadik_src_clk_init_debugfs); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci#endif 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic void __init of_nomadik_pll_setup(struct device_node *np) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci struct clk_hw *hw; 50562306a36Sopenharmony_ci const char *clk_name = np->name; 50662306a36Sopenharmony_ci const char *parent_name; 50762306a36Sopenharmony_ci u32 pll_id; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci if (!src_base) 51062306a36Sopenharmony_ci nomadik_src_init(); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (of_property_read_u32(np, "pll-id", &pll_id)) { 51362306a36Sopenharmony_ci pr_err("%s: PLL \"%s\" missing pll-id property\n", 51462306a36Sopenharmony_ci __func__, clk_name); 51562306a36Sopenharmony_ci return; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci parent_name = of_clk_get_parent_name(np, 0); 51862306a36Sopenharmony_ci hw = pll_clk_register(NULL, clk_name, parent_name, pll_id); 51962306a36Sopenharmony_ci if (!IS_ERR(hw)) 52062306a36Sopenharmony_ci of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ciCLK_OF_DECLARE(nomadik_pll_clk, 52362306a36Sopenharmony_ci "st,nomadik-pll-clock", of_nomadik_pll_setup); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic void __init of_nomadik_hclk_setup(struct device_node *np) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct clk_hw *hw; 52862306a36Sopenharmony_ci const char *clk_name = np->name; 52962306a36Sopenharmony_ci const char *parent_name; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (!src_base) 53262306a36Sopenharmony_ci nomadik_src_init(); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci parent_name = of_clk_get_parent_name(np, 0); 53562306a36Sopenharmony_ci /* 53662306a36Sopenharmony_ci * The HCLK divides PLL1 with 1 (passthru), 2, 3 or 4. 53762306a36Sopenharmony_ci */ 53862306a36Sopenharmony_ci hw = clk_hw_register_divider(NULL, clk_name, parent_name, 53962306a36Sopenharmony_ci 0, src_base + SRC_CR, 54062306a36Sopenharmony_ci 13, 2, 54162306a36Sopenharmony_ci CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, 54262306a36Sopenharmony_ci &src_lock); 54362306a36Sopenharmony_ci if (!IS_ERR(hw)) 54462306a36Sopenharmony_ci of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ciCLK_OF_DECLARE(nomadik_hclk_clk, 54762306a36Sopenharmony_ci "st,nomadik-hclk-clock", of_nomadik_hclk_setup); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cistatic void __init of_nomadik_src_clk_setup(struct device_node *np) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci struct clk_hw *hw; 55262306a36Sopenharmony_ci const char *clk_name = np->name; 55362306a36Sopenharmony_ci const char *parent_name; 55462306a36Sopenharmony_ci u32 clk_id; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (!src_base) 55762306a36Sopenharmony_ci nomadik_src_init(); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (of_property_read_u32(np, "clock-id", &clk_id)) { 56062306a36Sopenharmony_ci pr_err("%s: SRC clock \"%s\" missing clock-id property\n", 56162306a36Sopenharmony_ci __func__, clk_name); 56262306a36Sopenharmony_ci return; 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci parent_name = of_clk_get_parent_name(np, 0); 56562306a36Sopenharmony_ci hw = src_clk_register(NULL, clk_name, parent_name, clk_id); 56662306a36Sopenharmony_ci if (!IS_ERR(hw)) 56762306a36Sopenharmony_ci of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ciCLK_OF_DECLARE(nomadik_src_clk, 57062306a36Sopenharmony_ci "st,nomadik-src-clock", of_nomadik_src_clk_setup); 571