145 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*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 3116 17#define SYSTEM_MAX_NAME_SZ 3218 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 * __init107at91_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 else128 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