linux/drivers/clk/mmp/clk-gate.c

133 lines
2.9 KiB
C

/*
* mmp gate clock operation source file
*
* Copyright (C) 2014 Marvell
* Chao Xie <chao.xie@marvell.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/clk-provider.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/delay.h>
#include "clk.h"
/*
* Some clocks will have mutiple bits to enable the clocks, and
* the bits to disable the clock is not same as enabling bits.
*/
#define to_clk_mmp_gate(hw) container_of(hw, struct mmp_clk_gate, hw)
static int mmp_clk_gate_enable(struct clk_hw *hw)
{
struct mmp_clk_gate *gate = to_clk_mmp_gate(hw);
unsigned long flags = 0;
unsigned long rate;
u32 tmp;
if (gate->lock)
spin_lock_irqsave(gate->lock, flags);
tmp = readl(gate->reg);
tmp &= ~gate->mask;
tmp |= gate->val_enable;
writel(tmp, gate->reg);
if (gate->lock)
spin_unlock_irqrestore(gate->lock, flags);
if (gate->flags & MMP_CLK_GATE_NEED_DELAY) {
rate = clk_hw_get_rate(hw);
/* Need delay 2 cycles. */
udelay(2000000/rate);
}
return 0;
}
static void mmp_clk_gate_disable(struct clk_hw *hw)
{
struct mmp_clk_gate *gate = to_clk_mmp_gate(hw);
unsigned long flags = 0;
u32 tmp;
if (gate->lock)
spin_lock_irqsave(gate->lock, flags);
tmp = readl(gate->reg);
tmp &= ~gate->mask;
tmp |= gate->val_disable;
writel(tmp, gate->reg);
if (gate->lock)
spin_unlock_irqrestore(gate->lock, flags);
}
static int mmp_clk_gate_is_enabled(struct clk_hw *hw)
{
struct mmp_clk_gate *gate = to_clk_mmp_gate(hw);
unsigned long flags = 0;
u32 tmp;
if (gate->lock)
spin_lock_irqsave(gate->lock, flags);
tmp = readl(gate->reg);
if (gate->lock)
spin_unlock_irqrestore(gate->lock, flags);
return (tmp & gate->mask) == gate->val_enable;
}
const struct clk_ops mmp_clk_gate_ops = {
.enable = mmp_clk_gate_enable,
.disable = mmp_clk_gate_disable,
.is_enabled = mmp_clk_gate_is_enabled,
};
struct clk *mmp_clk_register_gate(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u32 mask, u32 val_enable, u32 val_disable,
unsigned int gate_flags, spinlock_t *lock)
{
struct mmp_clk_gate *gate;
struct clk *clk;
struct clk_init_data init;
/* allocate the gate */
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
if (!gate) {
pr_err("%s:%s could not allocate gate clk\n", __func__, name);
return ERR_PTR(-ENOMEM);
}
init.name = name;
init.ops = &mmp_clk_gate_ops;
init.flags = flags | CLK_IS_BASIC;
init.parent_names = (parent_name ? &parent_name : NULL);
init.num_parents = (parent_name ? 1 : 0);
/* struct clk_gate assignments */
gate->reg = reg;
gate->mask = mask;
gate->val_enable = val_enable;
gate->val_disable = val_disable;
gate->flags = gate_flags;
gate->lock = lock;
gate->hw.init = &init;
clk = clk_register(dev, &gate->hw);
if (IS_ERR(clk))
kfree(gate);
return clk;
}