Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Copyright 2018 NXP
0004  *  Dong Aisheng <aisheng.dong@nxp.com>
0005  */
0006 
0007 #include <linux/bits.h>
0008 #include <linux/clk-provider.h>
0009 #include <linux/err.h>
0010 #include <linux/io.h>
0011 #include <linux/slab.h>
0012 #include <linux/spinlock.h>
0013 
0014 #include "clk-scu.h"
0015 
0016 static DEFINE_SPINLOCK(imx_lpcg_scu_lock);
0017 
0018 #define CLK_GATE_SCU_LPCG_MASK      0x3
0019 #define CLK_GATE_SCU_LPCG_HW_SEL    BIT(0)
0020 #define CLK_GATE_SCU_LPCG_SW_SEL    BIT(1)
0021 
0022 /*
0023  * struct clk_lpcg_scu - Description of LPCG clock
0024  *
0025  * @hw: clk_hw of this LPCG
0026  * @reg: register of this LPCG clock
0027  * @bit_idx: bit index of this LPCG clock
0028  * @hw_gate: HW auto gate enable
0029  *
0030  * This structure describes one LPCG clock
0031  */
0032 struct clk_lpcg_scu {
0033     struct clk_hw hw;
0034     void __iomem *reg;
0035     u8 bit_idx;
0036     bool hw_gate;
0037 
0038     /* for state save&restore */
0039     u32 state;
0040 };
0041 
0042 #define to_clk_lpcg_scu(_hw) container_of(_hw, struct clk_lpcg_scu, hw)
0043 
0044 static int clk_lpcg_scu_enable(struct clk_hw *hw)
0045 {
0046     struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw);
0047     unsigned long flags;
0048     u32 reg, val;
0049 
0050     spin_lock_irqsave(&imx_lpcg_scu_lock, flags);
0051 
0052     reg = readl_relaxed(clk->reg);
0053     reg &= ~(CLK_GATE_SCU_LPCG_MASK << clk->bit_idx);
0054 
0055     val = CLK_GATE_SCU_LPCG_SW_SEL;
0056     if (clk->hw_gate)
0057         val |= CLK_GATE_SCU_LPCG_HW_SEL;
0058 
0059     reg |= val << clk->bit_idx;
0060     writel(reg, clk->reg);
0061 
0062     spin_unlock_irqrestore(&imx_lpcg_scu_lock, flags);
0063 
0064     return 0;
0065 }
0066 
0067 static void clk_lpcg_scu_disable(struct clk_hw *hw)
0068 {
0069     struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw);
0070     unsigned long flags;
0071     u32 reg;
0072 
0073     spin_lock_irqsave(&imx_lpcg_scu_lock, flags);
0074 
0075     reg = readl_relaxed(clk->reg);
0076     reg &= ~(CLK_GATE_SCU_LPCG_MASK << clk->bit_idx);
0077     writel(reg, clk->reg);
0078 
0079     spin_unlock_irqrestore(&imx_lpcg_scu_lock, flags);
0080 }
0081 
0082 static const struct clk_ops clk_lpcg_scu_ops = {
0083     .enable = clk_lpcg_scu_enable,
0084     .disable = clk_lpcg_scu_disable,
0085 };
0086 
0087 struct clk_hw *__imx_clk_lpcg_scu(struct device *dev, const char *name,
0088                   const char *parent_name, unsigned long flags,
0089                   void __iomem *reg, u8 bit_idx, bool hw_gate)
0090 {
0091     struct clk_lpcg_scu *clk;
0092     struct clk_init_data init;
0093     struct clk_hw *hw;
0094     int ret;
0095 
0096     clk = kzalloc(sizeof(*clk), GFP_KERNEL);
0097     if (!clk)
0098         return ERR_PTR(-ENOMEM);
0099 
0100     clk->reg = reg;
0101     clk->bit_idx = bit_idx;
0102     clk->hw_gate = hw_gate;
0103 
0104     init.name = name;
0105     init.ops = &clk_lpcg_scu_ops;
0106     init.flags = CLK_SET_RATE_PARENT | flags;
0107     init.parent_names = parent_name ? &parent_name : NULL;
0108     init.num_parents = parent_name ? 1 : 0;
0109 
0110     clk->hw.init = &init;
0111 
0112     hw = &clk->hw;
0113     ret = clk_hw_register(dev, hw);
0114     if (ret) {
0115         kfree(clk);
0116         hw = ERR_PTR(ret);
0117         return hw;
0118     }
0119 
0120     if (dev)
0121         dev_set_drvdata(dev, clk);
0122 
0123     return hw;
0124 }
0125 
0126 void imx_clk_lpcg_scu_unregister(struct clk_hw *hw)
0127 {
0128     struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw);
0129 
0130     clk_hw_unregister(&clk->hw);
0131     kfree(clk);
0132 }
0133 
0134 static int __maybe_unused imx_clk_lpcg_scu_suspend(struct device *dev)
0135 {
0136     struct clk_lpcg_scu *clk = dev_get_drvdata(dev);
0137 
0138     clk->state = readl_relaxed(clk->reg);
0139     dev_dbg(dev, "save lpcg state 0x%x\n", clk->state);
0140 
0141     return 0;
0142 }
0143 
0144 static int __maybe_unused imx_clk_lpcg_scu_resume(struct device *dev)
0145 {
0146     struct clk_lpcg_scu *clk = dev_get_drvdata(dev);
0147 
0148     /*
0149      * FIXME: Sometimes writes don't work unless the CPU issues
0150      * them twice
0151      */
0152 
0153     writel(clk->state, clk->reg);
0154     writel(clk->state, clk->reg);
0155     dev_dbg(dev, "restore lpcg state 0x%x\n", clk->state);
0156 
0157     return 0;
0158 }
0159 
0160 const struct dev_pm_ops imx_clk_lpcg_scu_pm_ops = {
0161     SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_clk_lpcg_scu_suspend,
0162                       imx_clk_lpcg_scu_resume)
0163 };