1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> 4 */ 5 6 #include <linux/clk-provider.h> 7 #include <linux/clkdev.h> 8 #include <linux/clk/at91_pmc.h> 9 #include <linux/of.h> 10 #include <linux/mfd/syscon.h> 11 #include <linux/regmap.h> 12 13 #include "pmc.h" 14 15 #define SYSTEM_MAX_ID 31 16 17 #define SYSTEM_MAX_NAME_SZ 32 18 19 #define to_clk_system(hw) container_of(hw, struct clk_system, hw) 20 struct clk_system { 21 struct clk_hw hw; 22 struct regmap *regmap; 23 struct at91_clk_pms pms; 24 u8 id; 25 }; 26 27 static inline int is_pck(int id) 28 { 29 return (id >= 8) && (id <= 15); 30 } 31 32 static inline bool clk_system_ready(struct regmap *regmap, int id) 33 { 34 unsigned int status; 35 36 regmap_read(regmap, AT91_PMC_SR, &status); 37 38 return !!(status & (1 << id)); 39 } 40 41 static int clk_system_prepare(struct clk_hw *hw) 42 { 43 struct clk_system *sys = to_clk_system(hw); 44 45 regmap_write(sys->regmap, AT91_PMC_SCER, 1 << sys->id); 46 47 if (!is_pck(sys->id)) 48 return 0; 49 50 while (!clk_system_ready(sys->regmap, sys->id)) 51 cpu_relax(); 52 53 return 0; 54 } 55 56 static void clk_system_unprepare(struct clk_hw *hw) 57 { 58 struct clk_system *sys = to_clk_system(hw); 59 60 regmap_write(sys->regmap, AT91_PMC_SCDR, 1 << sys->id); 61 } 62 63 static int clk_system_is_prepared(struct clk_hw *hw) 64 { 65 struct clk_system *sys = to_clk_system(hw); 66 unsigned int status; 67 68 regmap_read(sys->regmap, AT91_PMC_SCSR, &status); 69 70 if (!(status & (1 << sys->id))) 71 return 0; 72 73 if (!is_pck(sys->id)) 74 return 1; 75 76 regmap_read(sys->regmap, AT91_PMC_SR, &status); 77 78 return !!(status & (1 << sys->id)); 79 } 80 81 static int clk_system_save_context(struct clk_hw *hw) 82 { 83 struct clk_system *sys = to_clk_system(hw); 84 85 sys->pms.status = clk_system_is_prepared(hw); 86 87 return 0; 88 } 89 90 static void clk_system_restore_context(struct clk_hw *hw) 91 { 92 struct clk_system *sys = to_clk_system(hw); 93 94 if (sys->pms.status) 95 clk_system_prepare(&sys->hw); 96 } 97 98 static const struct clk_ops system_ops = { 99 .prepare = clk_system_prepare, 100 .unprepare = clk_system_unprepare, 101 .is_prepared = clk_system_is_prepared, 102 .save_context = clk_system_save_context, 103 .restore_context = clk_system_restore_context, 104 }; 105 106 struct clk_hw * __init 107 at91_clk_register_system(struct regmap *regmap, const char *name, 108 const char *parent_name, struct clk_hw *parent_hw, u8 id, 109 unsigned long flags) 110 { 111 struct clk_system *sys; 112 struct clk_hw *hw; 113 struct clk_init_data init = {}; 114 int ret; 115 116 if (!(parent_name || parent_hw) || id > SYSTEM_MAX_ID) 117 return ERR_PTR(-EINVAL); 118 119 sys = kzalloc(sizeof(*sys), GFP_KERNEL); 120 if (!sys) 121 return ERR_PTR(-ENOMEM); 122 123 init.name = name; 124 init.ops = &system_ops; 125 if (parent_hw) 126 init.parent_hws = (const struct clk_hw **)&parent_hw; 127 else 128 init.parent_names = &parent_name; 129 init.num_parents = 1; 130 init.flags = CLK_SET_RATE_PARENT | flags; 131 132 sys->id = id; 133 sys->hw.init = &init; 134 sys->regmap = regmap; 135 136 hw = &sys->hw; 137 ret = clk_hw_register(NULL, &sys->hw); 138 if (ret) { 139 kfree(sys); 140 hw = ERR_PTR(ret); 141 } 142 143 return hw; 144 } 145