Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
0004  */
0005 
0006 #include <linux/clk-provider.h>
0007 #include <linux/clkdev.h>
0008 #include <linux/clk/at91_pmc.h>
0009 #include <linux/of.h>
0010 #include <linux/mfd/syscon.h>
0011 #include <linux/regmap.h>
0012 
0013 #include "pmc.h"
0014 
0015 #define SYSTEM_MAX_ID       31
0016 
0017 #define SYSTEM_MAX_NAME_SZ  32
0018 
0019 #define to_clk_system(hw) container_of(hw, struct clk_system, hw)
0020 struct clk_system {
0021     struct clk_hw hw;
0022     struct regmap *regmap;
0023     struct at91_clk_pms pms;
0024     u8 id;
0025 };
0026 
0027 static inline int is_pck(int id)
0028 {
0029     return (id >= 8) && (id <= 15);
0030 }
0031 
0032 static inline bool clk_system_ready(struct regmap *regmap, int id)
0033 {
0034     unsigned int status;
0035 
0036     regmap_read(regmap, AT91_PMC_SR, &status);
0037 
0038     return !!(status & (1 << id));
0039 }
0040 
0041 static int clk_system_prepare(struct clk_hw *hw)
0042 {
0043     struct clk_system *sys = to_clk_system(hw);
0044 
0045     regmap_write(sys->regmap, AT91_PMC_SCER, 1 << sys->id);
0046 
0047     if (!is_pck(sys->id))
0048         return 0;
0049 
0050     while (!clk_system_ready(sys->regmap, sys->id))
0051         cpu_relax();
0052 
0053     return 0;
0054 }
0055 
0056 static void clk_system_unprepare(struct clk_hw *hw)
0057 {
0058     struct clk_system *sys = to_clk_system(hw);
0059 
0060     regmap_write(sys->regmap, AT91_PMC_SCDR, 1 << sys->id);
0061 }
0062 
0063 static int clk_system_is_prepared(struct clk_hw *hw)
0064 {
0065     struct clk_system *sys = to_clk_system(hw);
0066     unsigned int status;
0067 
0068     regmap_read(sys->regmap, AT91_PMC_SCSR, &status);
0069 
0070     if (!(status & (1 << sys->id)))
0071         return 0;
0072 
0073     if (!is_pck(sys->id))
0074         return 1;
0075 
0076     regmap_read(sys->regmap, AT91_PMC_SR, &status);
0077 
0078     return !!(status & (1 << sys->id));
0079 }
0080 
0081 static int clk_system_save_context(struct clk_hw *hw)
0082 {
0083     struct clk_system *sys = to_clk_system(hw);
0084 
0085     sys->pms.status = clk_system_is_prepared(hw);
0086 
0087     return 0;
0088 }
0089 
0090 static void clk_system_restore_context(struct clk_hw *hw)
0091 {
0092     struct clk_system *sys = to_clk_system(hw);
0093 
0094     if (sys->pms.status)
0095         clk_system_prepare(&sys->hw);
0096 }
0097 
0098 static const struct clk_ops system_ops = {
0099     .prepare = clk_system_prepare,
0100     .unprepare = clk_system_unprepare,
0101     .is_prepared = clk_system_is_prepared,
0102     .save_context = clk_system_save_context,
0103     .restore_context = clk_system_restore_context,
0104 };
0105 
0106 struct clk_hw * __init
0107 at91_clk_register_system(struct regmap *regmap, const char *name,
0108              const char *parent_name, u8 id)
0109 {
0110     struct clk_system *sys;
0111     struct clk_hw *hw;
0112     struct clk_init_data init;
0113     int ret;
0114 
0115     if (!parent_name || id > SYSTEM_MAX_ID)
0116         return ERR_PTR(-EINVAL);
0117 
0118     sys = kzalloc(sizeof(*sys), GFP_KERNEL);
0119     if (!sys)
0120         return ERR_PTR(-ENOMEM);
0121 
0122     init.name = name;
0123     init.ops = &system_ops;
0124     init.parent_names = &parent_name;
0125     init.num_parents = 1;
0126     init.flags = CLK_SET_RATE_PARENT;
0127 
0128     sys->id = id;
0129     sys->hw.init = &init;
0130     sys->regmap = regmap;
0131 
0132     hw = &sys->hw;
0133     ret = clk_hw_register(NULL, &sys->hw);
0134     if (ret) {
0135         kfree(sys);
0136         hw = ERR_PTR(ret);
0137     }
0138 
0139     return hw;
0140 }