linux/drivers/clk/at91/clk-system.c
<<
>>
Prefs
   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        u8 id;
  24};
  25
  26static inline int is_pck(int id)
  27{
  28        return (id >= 8) && (id <= 15);
  29}
  30
  31static 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));
  38}
  39
  40static 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
  55static 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
  62static 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));
  78}
  79
  80static 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
  86struct clk_hw * __init
  87at91_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