Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Copyright (C) 2016 Freescale Semiconductor, Inc.
0004  * Copyright 2017~2018 NXP
0005  *
0006  * Author: Dong Aisheng <aisheng.dong@nxp.com>
0007  *
0008  */
0009 
0010 #include <linux/clk-provider.h>
0011 #include <linux/err.h>
0012 #include <linux/io.h>
0013 #include <linux/iopoll.h>
0014 #include <linux/slab.h>
0015 
0016 #include "clk.h"
0017 
0018 /**
0019  * struct clk_pfdv2 - IMX PFD clock
0020  * @hw:     clock source
0021  * @reg:    PFD register address
0022  * @gate_bit:   Gate bit offset
0023  * @vld_bit:    Valid bit offset
0024  * @frac_off:   PLL Fractional Divider offset
0025  */
0026 
0027 struct clk_pfdv2 {
0028     struct clk_hw   hw;
0029     void __iomem    *reg;
0030     u8      gate_bit;
0031     u8      vld_bit;
0032     u8      frac_off;
0033 };
0034 
0035 #define to_clk_pfdv2(_hw) container_of(_hw, struct clk_pfdv2, hw)
0036 
0037 #define CLK_PFDV2_FRAC_MASK 0x3f
0038 
0039 #define LOCK_TIMEOUT_US     USEC_PER_MSEC
0040 
0041 static DEFINE_SPINLOCK(pfd_lock);
0042 
0043 static int clk_pfdv2_wait(struct clk_pfdv2 *pfd)
0044 {
0045     u32 val;
0046 
0047     return readl_poll_timeout(pfd->reg, val, val & (1 << pfd->vld_bit),
0048                   0, LOCK_TIMEOUT_US);
0049 }
0050 
0051 static int clk_pfdv2_enable(struct clk_hw *hw)
0052 {
0053     struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
0054     unsigned long flags;
0055     u32 val;
0056 
0057     spin_lock_irqsave(&pfd_lock, flags);
0058     val = readl_relaxed(pfd->reg);
0059     val &= ~(1 << pfd->gate_bit);
0060     writel_relaxed(val, pfd->reg);
0061     spin_unlock_irqrestore(&pfd_lock, flags);
0062 
0063     return clk_pfdv2_wait(pfd);
0064 }
0065 
0066 static void clk_pfdv2_disable(struct clk_hw *hw)
0067 {
0068     struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
0069     unsigned long flags;
0070     u32 val;
0071 
0072     spin_lock_irqsave(&pfd_lock, flags);
0073     val = readl_relaxed(pfd->reg);
0074     val |= (1 << pfd->gate_bit);
0075     writel_relaxed(val, pfd->reg);
0076     spin_unlock_irqrestore(&pfd_lock, flags);
0077 }
0078 
0079 static unsigned long clk_pfdv2_recalc_rate(struct clk_hw *hw,
0080                        unsigned long parent_rate)
0081 {
0082     struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
0083     u64 tmp = parent_rate;
0084     u8 frac;
0085 
0086     frac = (readl_relaxed(pfd->reg) >> pfd->frac_off)
0087         & CLK_PFDV2_FRAC_MASK;
0088 
0089     if (!frac) {
0090         pr_debug("clk_pfdv2: %s invalid pfd frac value 0\n",
0091              clk_hw_get_name(hw));
0092         return 0;
0093     }
0094 
0095     tmp *= 18;
0096     do_div(tmp, frac);
0097 
0098     return tmp;
0099 }
0100 
0101 static int clk_pfdv2_determine_rate(struct clk_hw *hw,
0102                     struct clk_rate_request *req)
0103 {
0104     unsigned long parent_rates[] = {
0105                     480000000,
0106                     528000000,
0107                     req->best_parent_rate
0108                        };
0109     unsigned long best_rate = -1UL, rate = req->rate;
0110     unsigned long best_parent_rate = req->best_parent_rate;
0111     u64 tmp;
0112     u8 frac;
0113     int i;
0114 
0115     for (i = 0; i < ARRAY_SIZE(parent_rates); i++) {
0116         tmp = parent_rates[i];
0117         tmp = tmp * 18 + rate / 2;
0118         do_div(tmp, rate);
0119         frac = tmp;
0120 
0121         if (frac < 12)
0122             frac = 12;
0123         else if (frac > 35)
0124             frac = 35;
0125 
0126         tmp = parent_rates[i];
0127         tmp *= 18;
0128         do_div(tmp, frac);
0129 
0130         if (abs(tmp - req->rate) < abs(best_rate - req->rate)) {
0131             best_rate = tmp;
0132             best_parent_rate = parent_rates[i];
0133         }
0134     }
0135 
0136     req->best_parent_rate = best_parent_rate;
0137     req->rate = best_rate;
0138 
0139     return 0;
0140 }
0141 
0142 static int clk_pfdv2_is_enabled(struct clk_hw *hw)
0143 {
0144     struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
0145 
0146     if (readl_relaxed(pfd->reg) & (1 << pfd->gate_bit))
0147         return 0;
0148 
0149     return 1;
0150 }
0151 
0152 static int clk_pfdv2_set_rate(struct clk_hw *hw, unsigned long rate,
0153                   unsigned long parent_rate)
0154 {
0155     struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
0156     unsigned long flags;
0157     u64 tmp = parent_rate;
0158     u32 val;
0159     u8 frac;
0160 
0161     if (!rate)
0162         return -EINVAL;
0163 
0164     /*
0165      * PFD can NOT change rate without gating.
0166      * as the PFDs may enabled in HW by default but no
0167      * consumer used it, the enable count is '0', so the
0168      * 'SET_RATE_GATE' can NOT help on blocking the set_rate
0169      * ops especially for 'assigned-clock-xxx'. In order
0170      * to simplify the case, just disable the PFD if it is
0171      * enabled in HW but not in SW.
0172      */
0173     if (clk_pfdv2_is_enabled(hw))
0174         clk_pfdv2_disable(hw);
0175 
0176     tmp = tmp * 18 + rate / 2;
0177     do_div(tmp, rate);
0178     frac = tmp;
0179     if (frac < 12)
0180         frac = 12;
0181     else if (frac > 35)
0182         frac = 35;
0183 
0184     spin_lock_irqsave(&pfd_lock, flags);
0185     val = readl_relaxed(pfd->reg);
0186     val &= ~(CLK_PFDV2_FRAC_MASK << pfd->frac_off);
0187     val |= frac << pfd->frac_off;
0188     writel_relaxed(val, pfd->reg);
0189     spin_unlock_irqrestore(&pfd_lock, flags);
0190 
0191     return 0;
0192 }
0193 
0194 static const struct clk_ops clk_pfdv2_ops = {
0195     .enable     = clk_pfdv2_enable,
0196     .disable    = clk_pfdv2_disable,
0197     .recalc_rate    = clk_pfdv2_recalc_rate,
0198     .determine_rate = clk_pfdv2_determine_rate,
0199     .set_rate   = clk_pfdv2_set_rate,
0200     .is_enabled     = clk_pfdv2_is_enabled,
0201 };
0202 
0203 struct clk_hw *imx_clk_hw_pfdv2(enum imx_pfdv2_type type, const char *name,
0204                  const char *parent_name, void __iomem *reg, u8 idx)
0205 {
0206     struct clk_init_data init;
0207     struct clk_pfdv2 *pfd;
0208     struct clk_hw *hw;
0209     int ret;
0210 
0211     WARN_ON(idx > 3);
0212 
0213     pfd = kzalloc(sizeof(*pfd), GFP_KERNEL);
0214     if (!pfd)
0215         return ERR_PTR(-ENOMEM);
0216 
0217     pfd->reg = reg;
0218     pfd->gate_bit = (idx + 1) * 8 - 1;
0219     pfd->vld_bit = pfd->gate_bit - 1;
0220     pfd->frac_off = idx * 8;
0221 
0222     init.name = name;
0223     init.ops = &clk_pfdv2_ops;
0224     init.parent_names = &parent_name;
0225     init.num_parents = 1;
0226     if (type == IMX_PFDV2_IMX7ULP)
0227         init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT;
0228     else
0229         init.flags = CLK_SET_RATE_GATE;
0230 
0231     pfd->hw.init = &init;
0232 
0233     hw = &pfd->hw;
0234     ret = clk_hw_register(NULL, hw);
0235     if (ret) {
0236         kfree(pfd);
0237         hw = ERR_PTR(ret);
0238     }
0239 
0240     return hw;
0241 }
0242 EXPORT_SYMBOL_GPL(imx_clk_hw_pfdv2);