162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only OR MIT 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for an SoC block (Numerically Controlled Oscillator) 462306a36Sopenharmony_ci * found on t8103 (M1) and other Apple chips 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) The Asahi Linux Contributors 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/bits.h> 1062306a36Sopenharmony_ci#include <linux/bitfield.h> 1162306a36Sopenharmony_ci#include <linux/clk-provider.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/math64.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/spinlock.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define NCO_CHANNEL_STRIDE 0x4000 2162306a36Sopenharmony_ci#define NCO_CHANNEL_REGSIZE 20 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define REG_CTRL 0 2462306a36Sopenharmony_ci#define CTRL_ENABLE BIT(31) 2562306a36Sopenharmony_ci#define REG_DIV 4 2662306a36Sopenharmony_ci#define DIV_FINE GENMASK(1, 0) 2762306a36Sopenharmony_ci#define DIV_COARSE GENMASK(12, 2) 2862306a36Sopenharmony_ci#define REG_INC1 8 2962306a36Sopenharmony_ci#define REG_INC2 12 3062306a36Sopenharmony_ci#define REG_ACCINIT 16 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* 3362306a36Sopenharmony_ci * Theory of operation (postulated) 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * The REG_DIV register indirectly expresses a base integer divisor, roughly 3662306a36Sopenharmony_ci * corresponding to twice the desired ratio of input to output clock. This 3762306a36Sopenharmony_ci * base divisor is adjusted on a cycle-by-cycle basis based on the state of a 3862306a36Sopenharmony_ci * 32-bit phase accumulator to achieve a desired precise clock ratio over the 3962306a36Sopenharmony_ci * long term. 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * Specifically an output clock cycle is produced after (REG_DIV divisor)/2 4262306a36Sopenharmony_ci * or (REG_DIV divisor + 1)/2 input cycles, the latter taking effect when top 4362306a36Sopenharmony_ci * bit of the 32-bit accumulator is set. The accumulator is incremented each 4462306a36Sopenharmony_ci * produced output cycle, by the value from either REG_INC1 or REG_INC2, which 4562306a36Sopenharmony_ci * of the two is selected depending again on the accumulator's current top bit. 4662306a36Sopenharmony_ci * 4762306a36Sopenharmony_ci * Because the NCO hardware implements counting of input clock cycles in part 4862306a36Sopenharmony_ci * in a Galois linear-feedback shift register, the higher bits of divisor 4962306a36Sopenharmony_ci * are programmed into REG_DIV by picking an appropriate LFSR state. See 5062306a36Sopenharmony_ci * applnco_compute_tables/applnco_div_translate for details on this. 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define LFSR_POLY 0xa01 5462306a36Sopenharmony_ci#define LFSR_INIT 0x7ff 5562306a36Sopenharmony_ci#define LFSR_LEN 11 5662306a36Sopenharmony_ci#define LFSR_PERIOD ((1 << LFSR_LEN) - 1) 5762306a36Sopenharmony_ci#define LFSR_TBLSIZE (1 << LFSR_LEN) 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* The minimal attainable coarse divisor (first value in table) */ 6062306a36Sopenharmony_ci#define COARSE_DIV_OFFSET 2 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistruct applnco_tables { 6362306a36Sopenharmony_ci u16 fwd[LFSR_TBLSIZE]; 6462306a36Sopenharmony_ci u16 inv[LFSR_TBLSIZE]; 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistruct applnco_channel { 6862306a36Sopenharmony_ci void __iomem *base; 6962306a36Sopenharmony_ci struct applnco_tables *tbl; 7062306a36Sopenharmony_ci struct clk_hw hw; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci spinlock_t lock; 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci#define to_applnco_channel(_hw) container_of(_hw, struct applnco_channel, hw) 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic void applnco_enable_nolock(struct clk_hw *hw) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci struct applnco_channel *chan = to_applnco_channel(hw); 8062306a36Sopenharmony_ci u32 val; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci val = readl_relaxed(chan->base + REG_CTRL); 8362306a36Sopenharmony_ci writel_relaxed(val | CTRL_ENABLE, chan->base + REG_CTRL); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic void applnco_disable_nolock(struct clk_hw *hw) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct applnco_channel *chan = to_applnco_channel(hw); 8962306a36Sopenharmony_ci u32 val; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci val = readl_relaxed(chan->base + REG_CTRL); 9262306a36Sopenharmony_ci writel_relaxed(val & ~CTRL_ENABLE, chan->base + REG_CTRL); 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic int applnco_is_enabled(struct clk_hw *hw) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci struct applnco_channel *chan = to_applnco_channel(hw); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci return (readl_relaxed(chan->base + REG_CTRL) & CTRL_ENABLE) != 0; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic void applnco_compute_tables(struct applnco_tables *tbl) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci int i; 10562306a36Sopenharmony_ci u32 state = LFSR_INIT; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* 10862306a36Sopenharmony_ci * Go through the states of a Galois LFSR and build 10962306a36Sopenharmony_ci * a coarse divisor translation table. 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_ci for (i = LFSR_PERIOD; i > 0; i--) { 11262306a36Sopenharmony_ci if (state & 1) 11362306a36Sopenharmony_ci state = (state >> 1) ^ (LFSR_POLY >> 1); 11462306a36Sopenharmony_ci else 11562306a36Sopenharmony_ci state = (state >> 1); 11662306a36Sopenharmony_ci tbl->fwd[i] = state; 11762306a36Sopenharmony_ci tbl->inv[state] = i; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* Zero value is special-cased */ 12162306a36Sopenharmony_ci tbl->fwd[0] = 0; 12262306a36Sopenharmony_ci tbl->inv[0] = 0; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic bool applnco_div_out_of_range(unsigned int div) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci unsigned int coarse = div / 4; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return coarse < COARSE_DIV_OFFSET || 13062306a36Sopenharmony_ci coarse >= COARSE_DIV_OFFSET + LFSR_TBLSIZE; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic u32 applnco_div_translate(struct applnco_tables *tbl, unsigned int div) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci unsigned int coarse = div / 4; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (WARN_ON(applnco_div_out_of_range(div))) 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci return FIELD_PREP(DIV_COARSE, tbl->fwd[coarse - COARSE_DIV_OFFSET]) | 14162306a36Sopenharmony_ci FIELD_PREP(DIV_FINE, div % 4); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic unsigned int applnco_div_translate_inv(struct applnco_tables *tbl, u32 regval) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci unsigned int coarse, fine; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci coarse = tbl->inv[FIELD_GET(DIV_COARSE, regval)] + COARSE_DIV_OFFSET; 14962306a36Sopenharmony_ci fine = FIELD_GET(DIV_FINE, regval); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return coarse * 4 + fine; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic int applnco_set_rate(struct clk_hw *hw, unsigned long rate, 15562306a36Sopenharmony_ci unsigned long parent_rate) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct applnco_channel *chan = to_applnco_channel(hw); 15862306a36Sopenharmony_ci unsigned long flags; 15962306a36Sopenharmony_ci u32 div, inc1, inc2; 16062306a36Sopenharmony_ci bool was_enabled; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci div = 2 * parent_rate / rate; 16362306a36Sopenharmony_ci inc1 = 2 * parent_rate - div * rate; 16462306a36Sopenharmony_ci inc2 = inc1 - rate; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (applnco_div_out_of_range(div)) 16762306a36Sopenharmony_ci return -EINVAL; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci div = applnco_div_translate(chan->tbl, div); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 17262306a36Sopenharmony_ci was_enabled = applnco_is_enabled(hw); 17362306a36Sopenharmony_ci applnco_disable_nolock(hw); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci writel_relaxed(div, chan->base + REG_DIV); 17662306a36Sopenharmony_ci writel_relaxed(inc1, chan->base + REG_INC1); 17762306a36Sopenharmony_ci writel_relaxed(inc2, chan->base + REG_INC2); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* Presumably a neutral initial value for accumulator */ 18062306a36Sopenharmony_ci writel_relaxed(1 << 31, chan->base + REG_ACCINIT); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (was_enabled) 18362306a36Sopenharmony_ci applnco_enable_nolock(hw); 18462306a36Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci return 0; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic unsigned long applnco_recalc_rate(struct clk_hw *hw, 19062306a36Sopenharmony_ci unsigned long parent_rate) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct applnco_channel *chan = to_applnco_channel(hw); 19362306a36Sopenharmony_ci u32 div, inc1, inc2, incbase; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci div = applnco_div_translate_inv(chan->tbl, 19662306a36Sopenharmony_ci readl_relaxed(chan->base + REG_DIV)); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci inc1 = readl_relaxed(chan->base + REG_INC1); 19962306a36Sopenharmony_ci inc2 = readl_relaxed(chan->base + REG_INC2); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* 20262306a36Sopenharmony_ci * We don't support wraparound of accumulator 20362306a36Sopenharmony_ci * nor the edge case of both increments being zero 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_ci if (inc1 >= (1 << 31) || inc2 < (1 << 31) || (inc1 == 0 && inc2 == 0)) 20662306a36Sopenharmony_ci return 0; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* Scale both sides of division by incbase to maintain precision */ 20962306a36Sopenharmony_ci incbase = inc1 - inc2; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci return div64_u64(((u64) parent_rate) * 2 * incbase, 21262306a36Sopenharmony_ci ((u64) div) * incbase + inc1); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic long applnco_round_rate(struct clk_hw *hw, unsigned long rate, 21662306a36Sopenharmony_ci unsigned long *parent_rate) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci unsigned long lo = *parent_rate / (COARSE_DIV_OFFSET + LFSR_TBLSIZE) + 1; 21962306a36Sopenharmony_ci unsigned long hi = *parent_rate / COARSE_DIV_OFFSET; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return clamp(rate, lo, hi); 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic int applnco_enable(struct clk_hw *hw) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci struct applnco_channel *chan = to_applnco_channel(hw); 22762306a36Sopenharmony_ci unsigned long flags; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 23062306a36Sopenharmony_ci applnco_enable_nolock(hw); 23162306a36Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return 0; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic void applnco_disable(struct clk_hw *hw) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct applnco_channel *chan = to_applnco_channel(hw); 23962306a36Sopenharmony_ci unsigned long flags; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 24262306a36Sopenharmony_ci applnco_disable_nolock(hw); 24362306a36Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic const struct clk_ops applnco_ops = { 24762306a36Sopenharmony_ci .set_rate = applnco_set_rate, 24862306a36Sopenharmony_ci .recalc_rate = applnco_recalc_rate, 24962306a36Sopenharmony_ci .round_rate = applnco_round_rate, 25062306a36Sopenharmony_ci .enable = applnco_enable, 25162306a36Sopenharmony_ci .disable = applnco_disable, 25262306a36Sopenharmony_ci .is_enabled = applnco_is_enabled, 25362306a36Sopenharmony_ci}; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic int applnco_probe(struct platform_device *pdev) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 25862306a36Sopenharmony_ci struct clk_parent_data pdata = { .index = 0 }; 25962306a36Sopenharmony_ci struct clk_init_data init; 26062306a36Sopenharmony_ci struct clk_hw_onecell_data *onecell_data; 26162306a36Sopenharmony_ci void __iomem *base; 26262306a36Sopenharmony_ci struct resource *res; 26362306a36Sopenharmony_ci struct applnco_tables *tbl; 26462306a36Sopenharmony_ci unsigned int nchannels; 26562306a36Sopenharmony_ci int ret, i; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 26862306a36Sopenharmony_ci if (IS_ERR(base)) 26962306a36Sopenharmony_ci return PTR_ERR(base); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (resource_size(res) < NCO_CHANNEL_REGSIZE) 27262306a36Sopenharmony_ci return -EINVAL; 27362306a36Sopenharmony_ci nchannels = (resource_size(res) - NCO_CHANNEL_REGSIZE) 27462306a36Sopenharmony_ci / NCO_CHANNEL_STRIDE + 1; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci onecell_data = devm_kzalloc(&pdev->dev, struct_size(onecell_data, hws, 27762306a36Sopenharmony_ci nchannels), GFP_KERNEL); 27862306a36Sopenharmony_ci if (!onecell_data) 27962306a36Sopenharmony_ci return -ENOMEM; 28062306a36Sopenharmony_ci onecell_data->num = nchannels; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci tbl = devm_kzalloc(&pdev->dev, sizeof(*tbl), GFP_KERNEL); 28362306a36Sopenharmony_ci if (!tbl) 28462306a36Sopenharmony_ci return -ENOMEM; 28562306a36Sopenharmony_ci applnco_compute_tables(tbl); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci for (i = 0; i < nchannels; i++) { 28862306a36Sopenharmony_ci struct applnco_channel *chan; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci chan = devm_kzalloc(&pdev->dev, sizeof(*chan), GFP_KERNEL); 29162306a36Sopenharmony_ci if (!chan) 29262306a36Sopenharmony_ci return -ENOMEM; 29362306a36Sopenharmony_ci chan->base = base + NCO_CHANNEL_STRIDE * i; 29462306a36Sopenharmony_ci chan->tbl = tbl; 29562306a36Sopenharmony_ci spin_lock_init(&chan->lock); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci memset(&init, 0, sizeof(init)); 29862306a36Sopenharmony_ci init.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, 29962306a36Sopenharmony_ci "%s-%d", np->name, i); 30062306a36Sopenharmony_ci init.ops = &applnco_ops; 30162306a36Sopenharmony_ci init.parent_data = &pdata; 30262306a36Sopenharmony_ci init.num_parents = 1; 30362306a36Sopenharmony_ci init.flags = 0; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci chan->hw.init = &init; 30662306a36Sopenharmony_ci ret = devm_clk_hw_register(&pdev->dev, &chan->hw); 30762306a36Sopenharmony_ci if (ret) 30862306a36Sopenharmony_ci return ret; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci onecell_data->hws[i] = &chan->hw; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get, 31462306a36Sopenharmony_ci onecell_data); 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic const struct of_device_id applnco_ids[] = { 31862306a36Sopenharmony_ci { .compatible = "apple,nco" }, 31962306a36Sopenharmony_ci { } 32062306a36Sopenharmony_ci}; 32162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, applnco_ids); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic struct platform_driver applnco_driver = { 32462306a36Sopenharmony_ci .driver = { 32562306a36Sopenharmony_ci .name = "apple-nco", 32662306a36Sopenharmony_ci .of_match_table = applnco_ids, 32762306a36Sopenharmony_ci }, 32862306a36Sopenharmony_ci .probe = applnco_probe, 32962306a36Sopenharmony_ci}; 33062306a36Sopenharmony_cimodule_platform_driver(applnco_driver); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ciMODULE_AUTHOR("Martin Povišer <povik+lin@cutebit.org>"); 33362306a36Sopenharmony_ciMODULE_DESCRIPTION("Clock driver for NCO blocks on Apple SoCs"); 33462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 335