Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  1/*
  2 * Copyright 2012 Freescale Semiconductor, Inc.
  3 * Copyright 2012 Linaro Ltd.
  4 *
  5 * The code contained herein is licensed under the GNU General Public
  6 * License. You may obtain a copy of the GNU General Public License
  7 * Version 2 or later at the following locations:
  8 *
  9 * http://www.opensource.org/licenses/gpl-license.html
 10 * http://www.gnu.org/copyleft/gpl.html
 11 */
 12
 13#include <linux/clk-provider.h>
 14#include <linux/io.h>
 15#include <linux/slab.h>
 16#include <linux/err.h>
 17#include "clk.h"
 18
 19/**
 20 * struct clk_pfd - IMX PFD clock
 21 * @clk_hw:	clock source
 22 * @reg:	PFD register address
 23 * @idx:	the index of PFD encoded in the register
 24 *
 25 * PFD clock found on i.MX6 series.  Each register for PFD has 4 clk_pfd
 26 * data encoded, and member idx is used to specify the one.  And each
 27 * register has SET, CLR and TOG registers at offset 0x4 0x8 and 0xc.
 28 */
 29struct clk_pfd {
 30	struct clk_hw	hw;
 31	void __iomem	*reg;
 32	u8		idx;
 33};
 34
 35#define to_clk_pfd(_hw) container_of(_hw, struct clk_pfd, hw)
 36
 37#define SET	0x4
 38#define CLR	0x8
 39#define OTG	0xc
 40
 41static int clk_pfd_enable(struct clk_hw *hw)
 42{
 43	struct clk_pfd *pfd = to_clk_pfd(hw);
 44
 45	writel_relaxed(1 << ((pfd->idx + 1) * 8 - 1), pfd->reg + CLR);
 46
 47	return 0;
 48}
 49
 50static void clk_pfd_disable(struct clk_hw *hw)
 51{
 52	struct clk_pfd *pfd = to_clk_pfd(hw);
 53
 54	writel_relaxed(1 << ((pfd->idx + 1) * 8 - 1), pfd->reg + SET);
 55}
 56
 57static unsigned long clk_pfd_recalc_rate(struct clk_hw *hw,
 58					 unsigned long parent_rate)
 59{
 60	struct clk_pfd *pfd = to_clk_pfd(hw);
 61	u64 tmp = parent_rate;
 62	u8 frac = (readl_relaxed(pfd->reg) >> (pfd->idx * 8)) & 0x3f;
 63
 64	tmp *= 18;
 65	do_div(tmp, frac);
 66
 67	return tmp;
 68}
 69
 70static long clk_pfd_round_rate(struct clk_hw *hw, unsigned long rate,
 71			       unsigned long *prate)
 72{
 73	u64 tmp = *prate;
 74	u8 frac;
 75
 76	tmp = tmp * 18 + rate / 2;
 77	do_div(tmp, rate);
 78	frac = tmp;
 79	if (frac < 12)
 80		frac = 12;
 81	else if (frac > 35)
 82		frac = 35;
 83	tmp = *prate;
 84	tmp *= 18;
 85	do_div(tmp, frac);
 86
 87	return tmp;
 88}
 89
 90static int clk_pfd_set_rate(struct clk_hw *hw, unsigned long rate,
 91		unsigned long parent_rate)
 92{
 93	struct clk_pfd *pfd = to_clk_pfd(hw);
 94	u64 tmp = parent_rate;
 95	u8 frac;
 96
 97	tmp = tmp * 18 + rate / 2;
 98	do_div(tmp, rate);
 99	frac = tmp;
100	if (frac < 12)
101		frac = 12;
102	else if (frac > 35)
103		frac = 35;
104
105	writel_relaxed(0x3f << (pfd->idx * 8), pfd->reg + CLR);
106	writel_relaxed(frac << (pfd->idx * 8), pfd->reg + SET);
107
108	return 0;
109}
110
111static int clk_pfd_is_enabled(struct clk_hw *hw)
112{
113	struct clk_pfd *pfd = to_clk_pfd(hw);
114
115	if (readl_relaxed(pfd->reg) & (1 << ((pfd->idx + 1) * 8 - 1)))
116		return 0;
117
118	return 1;
119}
120
121static const struct clk_ops clk_pfd_ops = {
122	.enable		= clk_pfd_enable,
123	.disable	= clk_pfd_disable,
124	.recalc_rate	= clk_pfd_recalc_rate,
125	.round_rate	= clk_pfd_round_rate,
126	.set_rate	= clk_pfd_set_rate,
127	.is_enabled     = clk_pfd_is_enabled,
128};
129
130struct clk *imx_clk_pfd(const char *name, const char *parent_name,
131			void __iomem *reg, u8 idx)
132{
133	struct clk_pfd *pfd;
134	struct clk *clk;
135	struct clk_init_data init;
136
137	pfd = kzalloc(sizeof(*pfd), GFP_KERNEL);
138	if (!pfd)
139		return ERR_PTR(-ENOMEM);
140
141	pfd->reg = reg;
142	pfd->idx = idx;
143
144	init.name = name;
145	init.ops = &clk_pfd_ops;
146	init.flags = 0;
147	init.parent_names = &parent_name;
148	init.num_parents = 1;
149
150	pfd->hw.init = &init;
151
152	clk = clk_register(NULL, &pfd->hw);
153	if (IS_ERR(clk))
154		kfree(pfd);
155
156	return clk;
157}