162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * MVEBU Core divider clock 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013 Marvell 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Ezequiel Garcia <ezequiel.garcia@free-electrons.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/clk-provider.h> 1362306a36Sopenharmony_ci#include <linux/io.h> 1462306a36Sopenharmony_ci#include <linux/of_address.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/delay.h> 1762306a36Sopenharmony_ci#include "common.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define CORE_CLK_DIV_RATIO_MASK 0xff 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* 2262306a36Sopenharmony_ci * This structure describes the hardware details (bit offset and mask) 2362306a36Sopenharmony_ci * to configure one particular core divider clock. Those hardware 2462306a36Sopenharmony_ci * details may differ from one SoC to another. This structure is 2562306a36Sopenharmony_ci * therefore typically instantiated statically to describe the 2662306a36Sopenharmony_ci * hardware details. 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_cistruct clk_corediv_desc { 2962306a36Sopenharmony_ci unsigned int mask; 3062306a36Sopenharmony_ci unsigned int offset; 3162306a36Sopenharmony_ci unsigned int fieldbit; 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * This structure describes the hardware details to configure the core 3662306a36Sopenharmony_ci * divider clocks on a given SoC. Amongst others, it points to the 3762306a36Sopenharmony_ci * array of core divider clock descriptors for this SoC, as well as 3862306a36Sopenharmony_ci * the corresponding operations to manipulate them. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_cistruct clk_corediv_soc_desc { 4162306a36Sopenharmony_ci const struct clk_corediv_desc *descs; 4262306a36Sopenharmony_ci unsigned int ndescs; 4362306a36Sopenharmony_ci const struct clk_ops ops; 4462306a36Sopenharmony_ci u32 ratio_reload; 4562306a36Sopenharmony_ci u32 enable_bit_offset; 4662306a36Sopenharmony_ci u32 ratio_offset; 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* 5062306a36Sopenharmony_ci * This structure represents one core divider clock for the clock 5162306a36Sopenharmony_ci * framework, and is dynamically allocated for each core divider clock 5262306a36Sopenharmony_ci * existing in the current SoC. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_cistruct clk_corediv { 5562306a36Sopenharmony_ci struct clk_hw hw; 5662306a36Sopenharmony_ci void __iomem *reg; 5762306a36Sopenharmony_ci const struct clk_corediv_desc *desc; 5862306a36Sopenharmony_ci const struct clk_corediv_soc_desc *soc_desc; 5962306a36Sopenharmony_ci spinlock_t lock; 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic struct clk_onecell_data clk_data; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* 6562306a36Sopenharmony_ci * Description of the core divider clocks available. For now, we 6662306a36Sopenharmony_ci * support only NAND, and it is available at the same register 6762306a36Sopenharmony_ci * locations regardless of the SoC. 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_cistatic const struct clk_corediv_desc mvebu_corediv_desc[] = { 7062306a36Sopenharmony_ci { .mask = 0x3f, .offset = 8, .fieldbit = 1 }, /* NAND clock */ 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic const struct clk_corediv_desc mv98dx3236_corediv_desc[] = { 7462306a36Sopenharmony_ci { .mask = 0x0f, .offset = 6, .fieldbit = 27 }, /* NAND clock */ 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define to_corediv_clk(p) container_of(p, struct clk_corediv, hw) 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic int clk_corediv_is_enabled(struct clk_hw *hwclk) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct clk_corediv *corediv = to_corediv_clk(hwclk); 8262306a36Sopenharmony_ci const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc; 8362306a36Sopenharmony_ci const struct clk_corediv_desc *desc = corediv->desc; 8462306a36Sopenharmony_ci u32 enable_mask = BIT(desc->fieldbit) << soc_desc->enable_bit_offset; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return !!(readl(corediv->reg) & enable_mask); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic int clk_corediv_enable(struct clk_hw *hwclk) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct clk_corediv *corediv = to_corediv_clk(hwclk); 9262306a36Sopenharmony_ci const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc; 9362306a36Sopenharmony_ci const struct clk_corediv_desc *desc = corediv->desc; 9462306a36Sopenharmony_ci unsigned long flags = 0; 9562306a36Sopenharmony_ci u32 reg; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci spin_lock_irqsave(&corediv->lock, flags); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci reg = readl(corediv->reg); 10062306a36Sopenharmony_ci reg |= (BIT(desc->fieldbit) << soc_desc->enable_bit_offset); 10162306a36Sopenharmony_ci writel(reg, corediv->reg); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci spin_unlock_irqrestore(&corediv->lock, flags); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci return 0; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic void clk_corediv_disable(struct clk_hw *hwclk) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct clk_corediv *corediv = to_corediv_clk(hwclk); 11162306a36Sopenharmony_ci const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc; 11262306a36Sopenharmony_ci const struct clk_corediv_desc *desc = corediv->desc; 11362306a36Sopenharmony_ci unsigned long flags = 0; 11462306a36Sopenharmony_ci u32 reg; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci spin_lock_irqsave(&corediv->lock, flags); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci reg = readl(corediv->reg); 11962306a36Sopenharmony_ci reg &= ~(BIT(desc->fieldbit) << soc_desc->enable_bit_offset); 12062306a36Sopenharmony_ci writel(reg, corediv->reg); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci spin_unlock_irqrestore(&corediv->lock, flags); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic unsigned long clk_corediv_recalc_rate(struct clk_hw *hwclk, 12662306a36Sopenharmony_ci unsigned long parent_rate) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci struct clk_corediv *corediv = to_corediv_clk(hwclk); 12962306a36Sopenharmony_ci const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc; 13062306a36Sopenharmony_ci const struct clk_corediv_desc *desc = corediv->desc; 13162306a36Sopenharmony_ci u32 reg, div; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci reg = readl(corediv->reg + soc_desc->ratio_offset); 13462306a36Sopenharmony_ci div = (reg >> desc->offset) & desc->mask; 13562306a36Sopenharmony_ci return parent_rate / div; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic long clk_corediv_round_rate(struct clk_hw *hwclk, unsigned long rate, 13962306a36Sopenharmony_ci unsigned long *parent_rate) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci /* Valid ratio are 1:4, 1:5, 1:6 and 1:8 */ 14262306a36Sopenharmony_ci u32 div; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci div = *parent_rate / rate; 14562306a36Sopenharmony_ci if (div < 4) 14662306a36Sopenharmony_ci div = 4; 14762306a36Sopenharmony_ci else if (div > 6) 14862306a36Sopenharmony_ci div = 8; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return *parent_rate / div; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic int clk_corediv_set_rate(struct clk_hw *hwclk, unsigned long rate, 15462306a36Sopenharmony_ci unsigned long parent_rate) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct clk_corediv *corediv = to_corediv_clk(hwclk); 15762306a36Sopenharmony_ci const struct clk_corediv_soc_desc *soc_desc = corediv->soc_desc; 15862306a36Sopenharmony_ci const struct clk_corediv_desc *desc = corediv->desc; 15962306a36Sopenharmony_ci unsigned long flags = 0; 16062306a36Sopenharmony_ci u32 reg, div; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci div = parent_rate / rate; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci spin_lock_irqsave(&corediv->lock, flags); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* Write new divider to the divider ratio register */ 16762306a36Sopenharmony_ci reg = readl(corediv->reg + soc_desc->ratio_offset); 16862306a36Sopenharmony_ci reg &= ~(desc->mask << desc->offset); 16962306a36Sopenharmony_ci reg |= (div & desc->mask) << desc->offset; 17062306a36Sopenharmony_ci writel(reg, corediv->reg + soc_desc->ratio_offset); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* Set reload-force for this clock */ 17362306a36Sopenharmony_ci reg = readl(corediv->reg) | BIT(desc->fieldbit); 17462306a36Sopenharmony_ci writel(reg, corediv->reg); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* Now trigger the clock update */ 17762306a36Sopenharmony_ci reg = readl(corediv->reg) | soc_desc->ratio_reload; 17862306a36Sopenharmony_ci writel(reg, corediv->reg); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* 18162306a36Sopenharmony_ci * Wait for clocks to settle down, and then clear all the 18262306a36Sopenharmony_ci * ratios request and the reload request. 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_ci udelay(1000); 18562306a36Sopenharmony_ci reg &= ~(CORE_CLK_DIV_RATIO_MASK | soc_desc->ratio_reload); 18662306a36Sopenharmony_ci writel(reg, corediv->reg); 18762306a36Sopenharmony_ci udelay(1000); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci spin_unlock_irqrestore(&corediv->lock, flags); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci return 0; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic const struct clk_corediv_soc_desc armada370_corediv_soc = { 19562306a36Sopenharmony_ci .descs = mvebu_corediv_desc, 19662306a36Sopenharmony_ci .ndescs = ARRAY_SIZE(mvebu_corediv_desc), 19762306a36Sopenharmony_ci .ops = { 19862306a36Sopenharmony_ci .enable = clk_corediv_enable, 19962306a36Sopenharmony_ci .disable = clk_corediv_disable, 20062306a36Sopenharmony_ci .is_enabled = clk_corediv_is_enabled, 20162306a36Sopenharmony_ci .recalc_rate = clk_corediv_recalc_rate, 20262306a36Sopenharmony_ci .round_rate = clk_corediv_round_rate, 20362306a36Sopenharmony_ci .set_rate = clk_corediv_set_rate, 20462306a36Sopenharmony_ci }, 20562306a36Sopenharmony_ci .ratio_reload = BIT(8), 20662306a36Sopenharmony_ci .enable_bit_offset = 24, 20762306a36Sopenharmony_ci .ratio_offset = 0x8, 20862306a36Sopenharmony_ci}; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic const struct clk_corediv_soc_desc armada380_corediv_soc = { 21162306a36Sopenharmony_ci .descs = mvebu_corediv_desc, 21262306a36Sopenharmony_ci .ndescs = ARRAY_SIZE(mvebu_corediv_desc), 21362306a36Sopenharmony_ci .ops = { 21462306a36Sopenharmony_ci .enable = clk_corediv_enable, 21562306a36Sopenharmony_ci .disable = clk_corediv_disable, 21662306a36Sopenharmony_ci .is_enabled = clk_corediv_is_enabled, 21762306a36Sopenharmony_ci .recalc_rate = clk_corediv_recalc_rate, 21862306a36Sopenharmony_ci .round_rate = clk_corediv_round_rate, 21962306a36Sopenharmony_ci .set_rate = clk_corediv_set_rate, 22062306a36Sopenharmony_ci }, 22162306a36Sopenharmony_ci .ratio_reload = BIT(8), 22262306a36Sopenharmony_ci .enable_bit_offset = 16, 22362306a36Sopenharmony_ci .ratio_offset = 0x4, 22462306a36Sopenharmony_ci}; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic const struct clk_corediv_soc_desc armada375_corediv_soc = { 22762306a36Sopenharmony_ci .descs = mvebu_corediv_desc, 22862306a36Sopenharmony_ci .ndescs = ARRAY_SIZE(mvebu_corediv_desc), 22962306a36Sopenharmony_ci .ops = { 23062306a36Sopenharmony_ci .recalc_rate = clk_corediv_recalc_rate, 23162306a36Sopenharmony_ci .round_rate = clk_corediv_round_rate, 23262306a36Sopenharmony_ci .set_rate = clk_corediv_set_rate, 23362306a36Sopenharmony_ci }, 23462306a36Sopenharmony_ci .ratio_reload = BIT(8), 23562306a36Sopenharmony_ci .ratio_offset = 0x4, 23662306a36Sopenharmony_ci}; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic const struct clk_corediv_soc_desc mv98dx3236_corediv_soc = { 23962306a36Sopenharmony_ci .descs = mv98dx3236_corediv_desc, 24062306a36Sopenharmony_ci .ndescs = ARRAY_SIZE(mv98dx3236_corediv_desc), 24162306a36Sopenharmony_ci .ops = { 24262306a36Sopenharmony_ci .recalc_rate = clk_corediv_recalc_rate, 24362306a36Sopenharmony_ci .round_rate = clk_corediv_round_rate, 24462306a36Sopenharmony_ci .set_rate = clk_corediv_set_rate, 24562306a36Sopenharmony_ci }, 24662306a36Sopenharmony_ci .ratio_reload = BIT(10), 24762306a36Sopenharmony_ci .ratio_offset = 0x8, 24862306a36Sopenharmony_ci}; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic void __init 25162306a36Sopenharmony_cimvebu_corediv_clk_init(struct device_node *node, 25262306a36Sopenharmony_ci const struct clk_corediv_soc_desc *soc_desc) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct clk_init_data init; 25562306a36Sopenharmony_ci struct clk_corediv *corediv; 25662306a36Sopenharmony_ci struct clk **clks; 25762306a36Sopenharmony_ci void __iomem *base; 25862306a36Sopenharmony_ci const char *parent_name; 25962306a36Sopenharmony_ci const char *clk_name; 26062306a36Sopenharmony_ci int i; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci base = of_iomap(node, 0); 26362306a36Sopenharmony_ci if (WARN_ON(!base)) 26462306a36Sopenharmony_ci return; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci parent_name = of_clk_get_parent_name(node, 0); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci clk_data.clk_num = soc_desc->ndescs; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* clks holds the clock array */ 27162306a36Sopenharmony_ci clks = kcalloc(clk_data.clk_num, sizeof(struct clk *), 27262306a36Sopenharmony_ci GFP_KERNEL); 27362306a36Sopenharmony_ci if (WARN_ON(!clks)) 27462306a36Sopenharmony_ci goto err_unmap; 27562306a36Sopenharmony_ci /* corediv holds the clock specific array */ 27662306a36Sopenharmony_ci corediv = kcalloc(clk_data.clk_num, sizeof(struct clk_corediv), 27762306a36Sopenharmony_ci GFP_KERNEL); 27862306a36Sopenharmony_ci if (WARN_ON(!corediv)) 27962306a36Sopenharmony_ci goto err_free_clks; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci spin_lock_init(&corediv->lock); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci for (i = 0; i < clk_data.clk_num; i++) { 28462306a36Sopenharmony_ci of_property_read_string_index(node, "clock-output-names", 28562306a36Sopenharmony_ci i, &clk_name); 28662306a36Sopenharmony_ci init.num_parents = 1; 28762306a36Sopenharmony_ci init.parent_names = &parent_name; 28862306a36Sopenharmony_ci init.name = clk_name; 28962306a36Sopenharmony_ci init.ops = &soc_desc->ops; 29062306a36Sopenharmony_ci init.flags = 0; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci corediv[i].soc_desc = soc_desc; 29362306a36Sopenharmony_ci corediv[i].desc = soc_desc->descs + i; 29462306a36Sopenharmony_ci corediv[i].reg = base; 29562306a36Sopenharmony_ci corediv[i].hw.init = &init; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci clks[i] = clk_register(NULL, &corediv[i].hw); 29862306a36Sopenharmony_ci WARN_ON(IS_ERR(clks[i])); 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci clk_data.clks = clks; 30262306a36Sopenharmony_ci of_clk_add_provider(node, of_clk_src_onecell_get, &clk_data); 30362306a36Sopenharmony_ci return; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cierr_free_clks: 30662306a36Sopenharmony_ci kfree(clks); 30762306a36Sopenharmony_cierr_unmap: 30862306a36Sopenharmony_ci iounmap(base); 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic void __init armada370_corediv_clk_init(struct device_node *node) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci return mvebu_corediv_clk_init(node, &armada370_corediv_soc); 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ciCLK_OF_DECLARE(armada370_corediv_clk, "marvell,armada-370-corediv-clock", 31662306a36Sopenharmony_ci armada370_corediv_clk_init); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic void __init armada375_corediv_clk_init(struct device_node *node) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci return mvebu_corediv_clk_init(node, &armada375_corediv_soc); 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ciCLK_OF_DECLARE(armada375_corediv_clk, "marvell,armada-375-corediv-clock", 32362306a36Sopenharmony_ci armada375_corediv_clk_init); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic void __init armada380_corediv_clk_init(struct device_node *node) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci return mvebu_corediv_clk_init(node, &armada380_corediv_soc); 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ciCLK_OF_DECLARE(armada380_corediv_clk, "marvell,armada-380-corediv-clock", 33062306a36Sopenharmony_ci armada380_corediv_clk_init); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic void __init mv98dx3236_corediv_clk_init(struct device_node *node) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci return mvebu_corediv_clk_init(node, &mv98dx3236_corediv_soc); 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ciCLK_OF_DECLARE(mv98dx3236_corediv_clk, "marvell,mv98dx3236-corediv-clock", 33762306a36Sopenharmony_ci mv98dx3236_corediv_clk_init); 338