1fcaf2036SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 223b5e15aSShawn Guo /* 323b5e15aSShawn Guo * Copyright 2012 Freescale Semiconductor, Inc. 423b5e15aSShawn Guo */ 523b5e15aSShawn Guo 623b5e15aSShawn Guo #include <linux/clk-provider.h> 723b5e15aSShawn Guo #include <linux/err.h> 823b5e15aSShawn Guo #include <linux/io.h> 923b5e15aSShawn Guo #include <linux/slab.h> 1023b5e15aSShawn Guo #include "clk.h" 1123b5e15aSShawn Guo 1223b5e15aSShawn Guo /** 1323b5e15aSShawn Guo * struct clk_ref - mxs reference clock 1423b5e15aSShawn Guo * @hw: clk_hw for the reference clock 1523b5e15aSShawn Guo * @reg: register address 1623b5e15aSShawn Guo * @idx: the index of the reference clock within the same register 1723b5e15aSShawn Guo * 1823b5e15aSShawn Guo * The mxs reference clock sources from pll. Every 4 reference clocks share 1923b5e15aSShawn Guo * one register space, and @idx is used to identify them. Each reference 2023b5e15aSShawn Guo * clock has a gate control and a fractional * divider. The rate is calculated 2123b5e15aSShawn Guo * as pll rate * (18 / FRAC), where FRAC = 18 ~ 35. 2223b5e15aSShawn Guo */ 2323b5e15aSShawn Guo struct clk_ref { 2423b5e15aSShawn Guo struct clk_hw hw; 2523b5e15aSShawn Guo void __iomem *reg; 2623b5e15aSShawn Guo u8 idx; 2723b5e15aSShawn Guo }; 2823b5e15aSShawn Guo 2923b5e15aSShawn Guo #define to_clk_ref(_hw) container_of(_hw, struct clk_ref, hw) 3023b5e15aSShawn Guo 3123b5e15aSShawn Guo static int clk_ref_enable(struct clk_hw *hw) 3223b5e15aSShawn Guo { 3323b5e15aSShawn Guo struct clk_ref *ref = to_clk_ref(hw); 3423b5e15aSShawn Guo 3523b5e15aSShawn Guo writel_relaxed(1 << ((ref->idx + 1) * 8 - 1), ref->reg + CLR); 3623b5e15aSShawn Guo 3723b5e15aSShawn Guo return 0; 3823b5e15aSShawn Guo } 3923b5e15aSShawn Guo 4023b5e15aSShawn Guo static void clk_ref_disable(struct clk_hw *hw) 4123b5e15aSShawn Guo { 4223b5e15aSShawn Guo struct clk_ref *ref = to_clk_ref(hw); 4323b5e15aSShawn Guo 4423b5e15aSShawn Guo writel_relaxed(1 << ((ref->idx + 1) * 8 - 1), ref->reg + SET); 4523b5e15aSShawn Guo } 4623b5e15aSShawn Guo 4723b5e15aSShawn Guo static unsigned long clk_ref_recalc_rate(struct clk_hw *hw, 4823b5e15aSShawn Guo unsigned long parent_rate) 4923b5e15aSShawn Guo { 5023b5e15aSShawn Guo struct clk_ref *ref = to_clk_ref(hw); 5123b5e15aSShawn Guo u64 tmp = parent_rate; 5223b5e15aSShawn Guo u8 frac = (readl_relaxed(ref->reg) >> (ref->idx * 8)) & 0x3f; 5323b5e15aSShawn Guo 5423b5e15aSShawn Guo tmp *= 18; 5523b5e15aSShawn Guo do_div(tmp, frac); 5623b5e15aSShawn Guo 5723b5e15aSShawn Guo return tmp; 5823b5e15aSShawn Guo } 5923b5e15aSShawn Guo 6023b5e15aSShawn Guo static long clk_ref_round_rate(struct clk_hw *hw, unsigned long rate, 6123b5e15aSShawn Guo unsigned long *prate) 6223b5e15aSShawn Guo { 6323b5e15aSShawn Guo unsigned long parent_rate = *prate; 6423b5e15aSShawn Guo u64 tmp = parent_rate; 6523b5e15aSShawn Guo u8 frac; 6623b5e15aSShawn Guo 6723b5e15aSShawn Guo tmp = tmp * 18 + rate / 2; 6823b5e15aSShawn Guo do_div(tmp, rate); 69*12b02b63SThorsten Blum frac = clamp(tmp, 18, 35); 7023b5e15aSShawn Guo 7123b5e15aSShawn Guo tmp = parent_rate; 7223b5e15aSShawn Guo tmp *= 18; 7323b5e15aSShawn Guo do_div(tmp, frac); 7423b5e15aSShawn Guo 7523b5e15aSShawn Guo return tmp; 7623b5e15aSShawn Guo } 7723b5e15aSShawn Guo 7823b5e15aSShawn Guo static int clk_ref_set_rate(struct clk_hw *hw, unsigned long rate, 7923b5e15aSShawn Guo unsigned long parent_rate) 8023b5e15aSShawn Guo { 8123b5e15aSShawn Guo struct clk_ref *ref = to_clk_ref(hw); 8223b5e15aSShawn Guo unsigned long flags; 8323b5e15aSShawn Guo u64 tmp = parent_rate; 8423b5e15aSShawn Guo u32 val; 8523b5e15aSShawn Guo u8 frac, shift = ref->idx * 8; 8623b5e15aSShawn Guo 8723b5e15aSShawn Guo tmp = tmp * 18 + rate / 2; 8823b5e15aSShawn Guo do_div(tmp, rate); 89*12b02b63SThorsten Blum frac = clamp(tmp, 18, 35); 9023b5e15aSShawn Guo 9123b5e15aSShawn Guo spin_lock_irqsave(&mxs_lock, flags); 9223b5e15aSShawn Guo 9323b5e15aSShawn Guo val = readl_relaxed(ref->reg); 9423b5e15aSShawn Guo val &= ~(0x3f << shift); 9523b5e15aSShawn Guo val |= frac << shift; 9623b5e15aSShawn Guo writel_relaxed(val, ref->reg); 9723b5e15aSShawn Guo 9823b5e15aSShawn Guo spin_unlock_irqrestore(&mxs_lock, flags); 9923b5e15aSShawn Guo 10023b5e15aSShawn Guo return 0; 10123b5e15aSShawn Guo } 10223b5e15aSShawn Guo 10323b5e15aSShawn Guo static const struct clk_ops clk_ref_ops = { 10423b5e15aSShawn Guo .enable = clk_ref_enable, 10523b5e15aSShawn Guo .disable = clk_ref_disable, 10623b5e15aSShawn Guo .recalc_rate = clk_ref_recalc_rate, 10723b5e15aSShawn Guo .round_rate = clk_ref_round_rate, 10823b5e15aSShawn Guo .set_rate = clk_ref_set_rate, 10923b5e15aSShawn Guo }; 11023b5e15aSShawn Guo 11123b5e15aSShawn Guo struct clk *mxs_clk_ref(const char *name, const char *parent_name, 11223b5e15aSShawn Guo void __iomem *reg, u8 idx) 11323b5e15aSShawn Guo { 11423b5e15aSShawn Guo struct clk_ref *ref; 11523b5e15aSShawn Guo struct clk *clk; 11623b5e15aSShawn Guo struct clk_init_data init; 11723b5e15aSShawn Guo 11823b5e15aSShawn Guo ref = kzalloc(sizeof(*ref), GFP_KERNEL); 11923b5e15aSShawn Guo if (!ref) 12023b5e15aSShawn Guo return ERR_PTR(-ENOMEM); 12123b5e15aSShawn Guo 12223b5e15aSShawn Guo init.name = name; 12323b5e15aSShawn Guo init.ops = &clk_ref_ops; 12423b5e15aSShawn Guo init.flags = 0; 12523b5e15aSShawn Guo init.parent_names = (parent_name ? &parent_name: NULL); 12623b5e15aSShawn Guo init.num_parents = (parent_name ? 1 : 0); 12723b5e15aSShawn Guo 12823b5e15aSShawn Guo ref->reg = reg; 12923b5e15aSShawn Guo ref->idx = idx; 13023b5e15aSShawn Guo ref->hw.init = &init; 13123b5e15aSShawn Guo 13223b5e15aSShawn Guo clk = clk_register(NULL, &ref->hw); 13323b5e15aSShawn Guo if (IS_ERR(clk)) 13423b5e15aSShawn Guo kfree(ref); 13523b5e15aSShawn Guo 13623b5e15aSShawn Guo return clk; 13723b5e15aSShawn Guo } 138