Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright 2015 Maxime Ripard
0004  *
0005  * Maxime Ripard <maxime.ripard@free-electrons.com>
0006  */
0007 
0008 #include <linux/clk-provider.h>
0009 #include <linux/io.h>
0010 #include <linux/of.h>
0011 #include <linux/of_address.h>
0012 #include <linux/slab.h>
0013 #include <linux/spinlock.h>
0014 
0015 #define TCON_CH1_SCLK2_PARENTS      4
0016 
0017 #define TCON_CH1_SCLK2_GATE_BIT     BIT(31)
0018 #define TCON_CH1_SCLK2_MUX_MASK     3
0019 #define TCON_CH1_SCLK2_MUX_SHIFT    24
0020 #define TCON_CH1_SCLK2_DIV_MASK     0xf
0021 #define TCON_CH1_SCLK2_DIV_SHIFT    0
0022 
0023 #define TCON_CH1_SCLK1_GATE_BIT     BIT(15)
0024 #define TCON_CH1_SCLK1_HALF_BIT     BIT(11)
0025 
0026 struct tcon_ch1_clk {
0027     struct clk_hw   hw;
0028     spinlock_t  lock;
0029     void __iomem    *reg;
0030 };
0031 
0032 #define hw_to_tclk(hw)  container_of(hw, struct tcon_ch1_clk, hw)
0033 
0034 static void tcon_ch1_disable(struct clk_hw *hw)
0035 {
0036     struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
0037     unsigned long flags;
0038     u32 reg;
0039 
0040     spin_lock_irqsave(&tclk->lock, flags);
0041     reg = readl(tclk->reg);
0042     reg &= ~(TCON_CH1_SCLK2_GATE_BIT | TCON_CH1_SCLK1_GATE_BIT);
0043     writel(reg, tclk->reg);
0044     spin_unlock_irqrestore(&tclk->lock, flags);
0045 }
0046 
0047 static int tcon_ch1_enable(struct clk_hw *hw)
0048 {
0049     struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
0050     unsigned long flags;
0051     u32 reg;
0052 
0053     spin_lock_irqsave(&tclk->lock, flags);
0054     reg = readl(tclk->reg);
0055     reg |= TCON_CH1_SCLK2_GATE_BIT | TCON_CH1_SCLK1_GATE_BIT;
0056     writel(reg, tclk->reg);
0057     spin_unlock_irqrestore(&tclk->lock, flags);
0058 
0059     return 0;
0060 }
0061 
0062 static int tcon_ch1_is_enabled(struct clk_hw *hw)
0063 {
0064     struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
0065     u32 reg;
0066 
0067     reg = readl(tclk->reg);
0068     return reg & (TCON_CH1_SCLK2_GATE_BIT | TCON_CH1_SCLK1_GATE_BIT);
0069 }
0070 
0071 static u8 tcon_ch1_get_parent(struct clk_hw *hw)
0072 {
0073     struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
0074     u32 reg;
0075 
0076     reg = readl(tclk->reg) >> TCON_CH1_SCLK2_MUX_SHIFT;
0077     reg &= reg >> TCON_CH1_SCLK2_MUX_MASK;
0078 
0079     return reg;
0080 }
0081 
0082 static int tcon_ch1_set_parent(struct clk_hw *hw, u8 index)
0083 {
0084     struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
0085     unsigned long flags;
0086     u32 reg;
0087 
0088     spin_lock_irqsave(&tclk->lock, flags);
0089     reg = readl(tclk->reg);
0090     reg &= ~(TCON_CH1_SCLK2_MUX_MASK << TCON_CH1_SCLK2_MUX_SHIFT);
0091     reg |= index << TCON_CH1_SCLK2_MUX_SHIFT;
0092     writel(reg, tclk->reg);
0093     spin_unlock_irqrestore(&tclk->lock, flags);
0094 
0095     return 0;
0096 };
0097 
0098 static unsigned long tcon_ch1_calc_divider(unsigned long rate,
0099                        unsigned long parent_rate,
0100                        u8 *div,
0101                        bool *half)
0102 {
0103     unsigned long best_rate = 0;
0104     u8 best_m = 0, m;
0105     bool is_double;
0106 
0107     for (m = 1; m < 16; m++) {
0108         u8 d;
0109 
0110         for (d = 1; d < 3; d++) {
0111             unsigned long tmp_rate;
0112 
0113             tmp_rate = parent_rate / m / d;
0114 
0115             if (tmp_rate > rate)
0116                 continue;
0117 
0118             if (!best_rate ||
0119                 (rate - tmp_rate) < (rate - best_rate)) {
0120                 best_rate = tmp_rate;
0121                 best_m = m;
0122                 is_double = d;
0123             }
0124         }
0125     }
0126 
0127     if (div && half) {
0128         *div = best_m;
0129         *half = is_double;
0130     }
0131 
0132     return best_rate;
0133 }
0134 
0135 static int tcon_ch1_determine_rate(struct clk_hw *hw,
0136                    struct clk_rate_request *req)
0137 {
0138     long best_rate = -EINVAL;
0139     int i;
0140 
0141     for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
0142         unsigned long parent_rate;
0143         unsigned long tmp_rate;
0144         struct clk_hw *parent;
0145 
0146         parent = clk_hw_get_parent_by_index(hw, i);
0147         if (!parent)
0148             continue;
0149 
0150         parent_rate = clk_hw_get_rate(parent);
0151 
0152         tmp_rate = tcon_ch1_calc_divider(req->rate, parent_rate,
0153                          NULL, NULL);
0154 
0155         if (best_rate < 0 ||
0156             (req->rate - tmp_rate) < (req->rate - best_rate)) {
0157             best_rate = tmp_rate;
0158             req->best_parent_rate = parent_rate;
0159             req->best_parent_hw = parent;
0160         }
0161     }
0162 
0163     if (best_rate < 0)
0164         return best_rate;
0165 
0166     req->rate = best_rate;
0167     return 0;
0168 }
0169 
0170 static unsigned long tcon_ch1_recalc_rate(struct clk_hw *hw,
0171                       unsigned long parent_rate)
0172 {
0173     struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
0174     u32 reg;
0175 
0176     reg = readl(tclk->reg);
0177 
0178     parent_rate /= (reg & TCON_CH1_SCLK2_DIV_MASK) + 1;
0179 
0180     if (reg & TCON_CH1_SCLK1_HALF_BIT)
0181         parent_rate /= 2;
0182 
0183     return parent_rate;
0184 }
0185 
0186 static int tcon_ch1_set_rate(struct clk_hw *hw, unsigned long rate,
0187                  unsigned long parent_rate)
0188 {
0189     struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
0190     unsigned long flags;
0191     bool half;
0192     u8 div_m;
0193     u32 reg;
0194 
0195     tcon_ch1_calc_divider(rate, parent_rate, &div_m, &half);
0196 
0197     spin_lock_irqsave(&tclk->lock, flags);
0198     reg = readl(tclk->reg);
0199     reg &= ~(TCON_CH1_SCLK2_DIV_MASK | TCON_CH1_SCLK1_HALF_BIT);
0200     reg |= (div_m - 1) & TCON_CH1_SCLK2_DIV_MASK;
0201 
0202     if (half)
0203         reg |= TCON_CH1_SCLK1_HALF_BIT;
0204 
0205     writel(reg, tclk->reg);
0206     spin_unlock_irqrestore(&tclk->lock, flags);
0207 
0208     return 0;
0209 }
0210 
0211 static const struct clk_ops tcon_ch1_ops = {
0212     .disable    = tcon_ch1_disable,
0213     .enable     = tcon_ch1_enable,
0214     .is_enabled = tcon_ch1_is_enabled,
0215 
0216     .get_parent = tcon_ch1_get_parent,
0217     .set_parent = tcon_ch1_set_parent,
0218 
0219     .determine_rate = tcon_ch1_determine_rate,
0220     .recalc_rate    = tcon_ch1_recalc_rate,
0221     .set_rate   = tcon_ch1_set_rate,
0222 };
0223 
0224 static void __init tcon_ch1_setup(struct device_node *node)
0225 {
0226     const char *parents[TCON_CH1_SCLK2_PARENTS];
0227     const char *clk_name = node->name;
0228     struct clk_init_data init;
0229     struct tcon_ch1_clk *tclk;
0230     struct resource res;
0231     struct clk *clk;
0232     void __iomem *reg;
0233     int ret;
0234 
0235     of_property_read_string(node, "clock-output-names", &clk_name);
0236 
0237     reg = of_io_request_and_map(node, 0, of_node_full_name(node));
0238     if (IS_ERR(reg)) {
0239         pr_err("%s: Could not map the clock registers\n", clk_name);
0240         return;
0241     }
0242 
0243     ret = of_clk_parent_fill(node, parents, TCON_CH1_SCLK2_PARENTS);
0244     if (ret != TCON_CH1_SCLK2_PARENTS) {
0245         pr_err("%s Could not retrieve the parents\n", clk_name);
0246         goto err_unmap;
0247     }
0248 
0249     tclk = kzalloc(sizeof(*tclk), GFP_KERNEL);
0250     if (!tclk)
0251         goto err_unmap;
0252 
0253     init.name = clk_name;
0254     init.ops = &tcon_ch1_ops;
0255     init.parent_names = parents;
0256     init.num_parents = TCON_CH1_SCLK2_PARENTS;
0257     init.flags = CLK_SET_RATE_PARENT;
0258 
0259     tclk->reg = reg;
0260     tclk->hw.init = &init;
0261     spin_lock_init(&tclk->lock);
0262 
0263     clk = clk_register(NULL, &tclk->hw);
0264     if (IS_ERR(clk)) {
0265         pr_err("%s: Couldn't register the clock\n", clk_name);
0266         goto err_free_data;
0267     }
0268 
0269     ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
0270     if (ret) {
0271         pr_err("%s: Couldn't register our clock provider\n", clk_name);
0272         goto err_unregister_clk;
0273     }
0274 
0275     return;
0276 
0277 err_unregister_clk:
0278     clk_unregister(clk);
0279 err_free_data:
0280     kfree(tclk);
0281 err_unmap:
0282     iounmap(reg);
0283     of_address_to_resource(node, 0, &res);
0284     release_mem_region(res.start, resource_size(&res));
0285 }
0286 
0287 CLK_OF_DECLARE(tcon_ch1, "allwinner,sun4i-a10-tcon-ch1-clk",
0288            tcon_ch1_setup);