1 /* 2 * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 */ 10 11 #include <linux/clk-provider.h> 12 #include <linux/clkdev.h> 13 #include <linux/clk/at91_pmc.h> 14 #include <linux/of.h> 15 #include <linux/mfd/syscon.h> 16 #include <linux/regmap.h> 17 18 #include "pmc.h" 19 20 #define SYSTEM_MAX_ID 31 21 22 #define SYSTEM_MAX_NAME_SZ 32 23 24 #define to_clk_system(hw) container_of(hw, struct clk_system, hw) 25 struct clk_system { 26 struct clk_hw hw; 27 struct regmap *regmap; 28 u8 id; 29 }; 30 31 static inline int is_pck(int id) 32 { 33 return (id >= 8) && (id <= 15); 34 } 35 36 static inline bool clk_system_ready(struct regmap *regmap, int id) 37 { 38 unsigned int status; 39 40 regmap_read(regmap, AT91_PMC_SR, &status); 41 42 return status & (1 << id) ? 1 : 0; 43 } 44 45 static int clk_system_prepare(struct clk_hw *hw) 46 { 47 struct clk_system *sys = to_clk_system(hw); 48 49 regmap_write(sys->regmap, AT91_PMC_SCER, 1 << sys->id); 50 51 if (!is_pck(sys->id)) 52 return 0; 53 54 while (!clk_system_ready(sys->regmap, sys->id)) 55 cpu_relax(); 56 57 return 0; 58 } 59 60 static void clk_system_unprepare(struct clk_hw *hw) 61 { 62 struct clk_system *sys = to_clk_system(hw); 63 64 regmap_write(sys->regmap, AT91_PMC_SCDR, 1 << sys->id); 65 } 66 67 static int clk_system_is_prepared(struct clk_hw *hw) 68 { 69 struct clk_system *sys = to_clk_system(hw); 70 unsigned int status; 71 72 regmap_read(sys->regmap, AT91_PMC_SCSR, &status); 73 74 if (!(status & (1 << sys->id))) 75 return 0; 76 77 if (!is_pck(sys->id)) 78 return 1; 79 80 regmap_read(sys->regmap, AT91_PMC_SR, &status); 81 82 return status & (1 << sys->id) ? 1 : 0; 83 } 84 85 static const struct clk_ops system_ops = { 86 .prepare = clk_system_prepare, 87 .unprepare = clk_system_unprepare, 88 .is_prepared = clk_system_is_prepared, 89 }; 90 91 struct clk_hw * __init 92 at91_clk_register_system(struct regmap *regmap, const char *name, 93 const char *parent_name, u8 id) 94 { 95 struct clk_system *sys; 96 struct clk_hw *hw; 97 struct clk_init_data init; 98 int ret; 99 100 if (!parent_name || id > SYSTEM_MAX_ID) 101 return ERR_PTR(-EINVAL); 102 103 sys = kzalloc(sizeof(*sys), GFP_KERNEL); 104 if (!sys) 105 return ERR_PTR(-ENOMEM); 106 107 init.name = name; 108 init.ops = &system_ops; 109 init.parent_names = &parent_name; 110 init.num_parents = 1; 111 init.flags = CLK_SET_RATE_PARENT; 112 113 sys->id = id; 114 sys->hw.init = &init; 115 sys->regmap = regmap; 116 117 hw = &sys->hw; 118 ret = clk_hw_register(NULL, &sys->hw); 119 if (ret) { 120 kfree(sys); 121 hw = ERR_PTR(ret); 122 } 123 124 return hw; 125 } 126