Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
  3 *
  4 * This program is free software; you can redistribute it and/or modify
  5 * it under the terms of the GNU General Public License as published by
  6 * the Free Software Foundation; either version 2 of the License, or
  7 * (at your option) any later version.
  8 *
  9 */
 10
 11#include <linux/clk-provider.h>
 12#include <linux/clkdev.h>
 13#include <linux/clk/at91_pmc.h>
 14#include <linux/of.h>
 15#include <linux/mfd/syscon.h>
 16#include <linux/regmap.h>
 17
 18#include "pmc.h"
 19
 20#define SYSTEM_MAX_ID		31
 21
 22#define SYSTEM_MAX_NAME_SZ	32
 23
 24#define to_clk_system(hw) container_of(hw, struct clk_system, hw)
 25struct clk_system {
 26	struct clk_hw hw;
 27	struct regmap *regmap;
 28	u8 id;
 29};
 30
 31static inline int is_pck(int id)
 32{
 33	return (id >= 8) && (id <= 15);
 34}
 35
 36static inline bool clk_system_ready(struct regmap *regmap, int id)
 37{
 38	unsigned int status;
 39
 40	regmap_read(regmap, AT91_PMC_SR, &status);
 41
 42	return status & (1 << id) ? 1 : 0;
 43}
 44
 45static int clk_system_prepare(struct clk_hw *hw)
 46{
 47	struct clk_system *sys = to_clk_system(hw);
 48
 49	regmap_write(sys->regmap, AT91_PMC_SCER, 1 << sys->id);
 50
 51	if (!is_pck(sys->id))
 52		return 0;
 53
 54	while (!clk_system_ready(sys->regmap, sys->id))
 55		cpu_relax();
 56
 57	return 0;
 58}
 59
 60static void clk_system_unprepare(struct clk_hw *hw)
 61{
 62	struct clk_system *sys = to_clk_system(hw);
 63
 64	regmap_write(sys->regmap, AT91_PMC_SCDR, 1 << sys->id);
 65}
 66
 67static int clk_system_is_prepared(struct clk_hw *hw)
 68{
 69	struct clk_system *sys = to_clk_system(hw);
 70	unsigned int status;
 71
 72	regmap_read(sys->regmap, AT91_PMC_SCSR, &status);
 73
 74	if (!(status & (1 << sys->id)))
 75		return 0;
 76
 77	if (!is_pck(sys->id))
 78		return 1;
 79
 80	regmap_read(sys->regmap, AT91_PMC_SR, &status);
 81
 82	return status & (1 << sys->id) ? 1 : 0;
 83}
 84
 85static const struct clk_ops system_ops = {
 86	.prepare = clk_system_prepare,
 87	.unprepare = clk_system_unprepare,
 88	.is_prepared = clk_system_is_prepared,
 89};
 90
 91static struct clk_hw * __init
 92at91_clk_register_system(struct regmap *regmap, const char *name,
 93			 const char *parent_name, u8 id)
 94{
 95	struct clk_system *sys;
 96	struct clk_hw *hw;
 97	struct clk_init_data init;
 98	int ret;
 99
100	if (!parent_name || id > SYSTEM_MAX_ID)
101		return ERR_PTR(-EINVAL);
102
103	sys = kzalloc(sizeof(*sys), GFP_KERNEL);
104	if (!sys)
105		return ERR_PTR(-ENOMEM);
106
107	init.name = name;
108	init.ops = &system_ops;
109	init.parent_names = &parent_name;
110	init.num_parents = 1;
111	init.flags = CLK_SET_RATE_PARENT;
112
113	sys->id = id;
114	sys->hw.init = &init;
115	sys->regmap = regmap;
116
117	hw = &sys->hw;
118	ret = clk_hw_register(NULL, &sys->hw);
119	if (ret) {
120		kfree(sys);
121		hw = ERR_PTR(ret);
122	}
123
124	return hw;
125}
126
127static void __init of_at91rm9200_clk_sys_setup(struct device_node *np)
128{
129	int num;
130	u32 id;
131	struct clk_hw *hw;
132	const char *name;
133	struct device_node *sysclknp;
134	const char *parent_name;
135	struct regmap *regmap;
136
137	num = of_get_child_count(np);
138	if (num > (SYSTEM_MAX_ID + 1))
139		return;
140
141	regmap = syscon_node_to_regmap(of_get_parent(np));
142	if (IS_ERR(regmap))
143		return;
144
145	for_each_child_of_node(np, sysclknp) {
146		if (of_property_read_u32(sysclknp, "reg", &id))
147			continue;
148
149		if (of_property_read_string(np, "clock-output-names", &name))
150			name = sysclknp->name;
151
152		parent_name = of_clk_get_parent_name(sysclknp, 0);
153
154		hw = at91_clk_register_system(regmap, name, parent_name, id);
155		if (IS_ERR(hw))
156			continue;
157
158		of_clk_add_hw_provider(sysclknp, of_clk_hw_simple_get, hw);
159	}
160}
161CLK_OF_DECLARE(at91rm9200_clk_sys, "atmel,at91rm9200-clk-system",
162	       of_at91rm9200_clk_sys_setup);