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 u8 id; 24 }; 25 26 static inline int is_pck(int id) 27 { 28 return (id >= 8) && (id <= 15); 29 } 30 31 static inline bool clk_system_ready(struct regmap *regmap, int id) 32 { 33 unsigned int status; 34 35 regmap_read(regmap, AT91_PMC_SR, &status); 36 37 return status & (1 << id) ? 1 : 0; 38 } 39 40 static int clk_system_prepare(struct clk_hw *hw) 41 { 42 struct clk_system *sys = to_clk_system(hw); 43 44 regmap_write(sys->regmap, AT91_PMC_SCER, 1 << sys->id); 45 46 if (!is_pck(sys->id)) 47 return 0; 48 49 while (!clk_system_ready(sys->regmap, sys->id)) 50 cpu_relax(); 51 52 return 0; 53 } 54 55 static void clk_system_unprepare(struct clk_hw *hw) 56 { 57 struct clk_system *sys = to_clk_system(hw); 58 59 regmap_write(sys->regmap, AT91_PMC_SCDR, 1 << sys->id); 60 } 61 62 static int clk_system_is_prepared(struct clk_hw *hw) 63 { 64 struct clk_system *sys = to_clk_system(hw); 65 unsigned int status; 66 67 regmap_read(sys->regmap, AT91_PMC_SCSR, &status); 68 69 if (!(status & (1 << sys->id))) 70 return 0; 71 72 if (!is_pck(sys->id)) 73 return 1; 74 75 regmap_read(sys->regmap, AT91_PMC_SR, &status); 76 77 return status & (1 << sys->id) ? 1 : 0; 78 } 79 80 static const struct clk_ops system_ops = { 81 .prepare = clk_system_prepare, 82 .unprepare = clk_system_unprepare, 83 .is_prepared = clk_system_is_prepared, 84 }; 85 86 struct clk_hw * __init 87 at91_clk_register_system(struct regmap *regmap, const char *name, 88 const char *parent_name, u8 id) 89 { 90 struct clk_system *sys; 91 struct clk_hw *hw; 92 struct clk_init_data init; 93 int ret; 94 95 if (!parent_name || id > SYSTEM_MAX_ID) 96 return ERR_PTR(-EINVAL); 97 98 sys = kzalloc(sizeof(*sys), GFP_KERNEL); 99 if (!sys) 100 return ERR_PTR(-ENOMEM); 101 102 init.name = name; 103 init.ops = &system_ops; 104 init.parent_names = &parent_name; 105 init.num_parents = 1; 106 init.flags = CLK_SET_RATE_PARENT; 107 108 sys->id = id; 109 sys->hw.init = &init; 110 sys->regmap = regmap; 111 112 hw = &sys->hw; 113 ret = clk_hw_register(NULL, &sys->hw); 114 if (ret) { 115 kfree(sys); 116 hw = ERR_PTR(ret); 117 } 118 119 return hw; 120 } 121