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