clk-system.c (2804B)
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) 20struct clk_system { 21 struct clk_hw hw; 22 struct regmap *regmap; 23 struct at91_clk_pms pms; 24 u8 id; 25}; 26 27static inline int is_pck(int id) 28{ 29 return (id >= 8) && (id <= 15); 30} 31 32static 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 41static 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 56static 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 63static 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 81static 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 90static 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 98static 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 106struct clk_hw * __init 107at91_clk_register_system(struct regmap *regmap, const char *name, 108 const char *parent_name, u8 id) 109{ 110 struct clk_system *sys; 111 struct clk_hw *hw; 112 struct clk_init_data init; 113 int ret; 114 115 if (!parent_name || id > SYSTEM_MAX_ID) 116 return ERR_PTR(-EINVAL); 117 118 sys = kzalloc(sizeof(*sys), GFP_KERNEL); 119 if (!sys) 120 return ERR_PTR(-ENOMEM); 121 122 init.name = name; 123 init.ops = &system_ops; 124 init.parent_names = &parent_name; 125 init.num_parents = 1; 126 init.flags = CLK_SET_RATE_PARENT; 127 128 sys->id = id; 129 sys->hw.init = &init; 130 sys->regmap = regmap; 131 132 hw = &sys->hw; 133 ret = clk_hw_register(NULL, &sys->hw); 134 if (ret) { 135 kfree(sys); 136 hw = ERR_PTR(ret); 137 } 138 139 return hw; 140}