Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  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, 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	else
128		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}