18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * mmp gate clock operation source file
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2014 Marvell
58c2ecf20Sopenharmony_ci * Chao Xie <chao.xie@marvell.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public
88c2ecf20Sopenharmony_ci * License version 2. This program is licensed "as is" without any
98c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/io.h>
158c2ecf20Sopenharmony_ci#include <linux/err.h>
168c2ecf20Sopenharmony_ci#include <linux/delay.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "clk.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/*
218c2ecf20Sopenharmony_ci * Some clocks will have mutiple bits to enable the clocks, and
228c2ecf20Sopenharmony_ci * the bits to disable the clock is not same as enabling bits.
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define to_clk_mmp_gate(hw)	container_of(hw, struct mmp_clk_gate, hw)
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic int mmp_clk_gate_enable(struct clk_hw *hw)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	struct mmp_clk_gate *gate = to_clk_mmp_gate(hw);
308c2ecf20Sopenharmony_ci	unsigned long flags = 0;
318c2ecf20Sopenharmony_ci	unsigned long rate;
328c2ecf20Sopenharmony_ci	u32 tmp;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	if (gate->lock)
358c2ecf20Sopenharmony_ci		spin_lock_irqsave(gate->lock, flags);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	tmp = readl(gate->reg);
388c2ecf20Sopenharmony_ci	tmp &= ~gate->mask;
398c2ecf20Sopenharmony_ci	tmp |= gate->val_enable;
408c2ecf20Sopenharmony_ci	writel(tmp, gate->reg);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	if (gate->lock)
438c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(gate->lock, flags);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	if (gate->flags & MMP_CLK_GATE_NEED_DELAY) {
468c2ecf20Sopenharmony_ci		rate = clk_hw_get_rate(hw);
478c2ecf20Sopenharmony_ci		/* Need delay 2 cycles. */
488c2ecf20Sopenharmony_ci		udelay(2000000/rate);
498c2ecf20Sopenharmony_ci	}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	return 0;
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic void mmp_clk_gate_disable(struct clk_hw *hw)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	struct mmp_clk_gate *gate = to_clk_mmp_gate(hw);
578c2ecf20Sopenharmony_ci	unsigned long flags = 0;
588c2ecf20Sopenharmony_ci	u32 tmp;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	if (gate->lock)
618c2ecf20Sopenharmony_ci		spin_lock_irqsave(gate->lock, flags);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	tmp = readl(gate->reg);
648c2ecf20Sopenharmony_ci	tmp &= ~gate->mask;
658c2ecf20Sopenharmony_ci	tmp |= gate->val_disable;
668c2ecf20Sopenharmony_ci	writel(tmp, gate->reg);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	if (gate->lock)
698c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(gate->lock, flags);
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic int mmp_clk_gate_is_enabled(struct clk_hw *hw)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	struct mmp_clk_gate *gate = to_clk_mmp_gate(hw);
758c2ecf20Sopenharmony_ci	unsigned long flags = 0;
768c2ecf20Sopenharmony_ci	u32 tmp;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	if (gate->lock)
798c2ecf20Sopenharmony_ci		spin_lock_irqsave(gate->lock, flags);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	tmp = readl(gate->reg);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	if (gate->lock)
848c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(gate->lock, flags);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	return (tmp & gate->mask) == gate->val_enable;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ciconst struct clk_ops mmp_clk_gate_ops = {
908c2ecf20Sopenharmony_ci	.enable = mmp_clk_gate_enable,
918c2ecf20Sopenharmony_ci	.disable = mmp_clk_gate_disable,
928c2ecf20Sopenharmony_ci	.is_enabled = mmp_clk_gate_is_enabled,
938c2ecf20Sopenharmony_ci};
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistruct clk *mmp_clk_register_gate(struct device *dev, const char *name,
968c2ecf20Sopenharmony_ci		const char *parent_name, unsigned long flags,
978c2ecf20Sopenharmony_ci		void __iomem *reg, u32 mask, u32 val_enable, u32 val_disable,
988c2ecf20Sopenharmony_ci		unsigned int gate_flags, spinlock_t *lock)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	struct mmp_clk_gate *gate;
1018c2ecf20Sopenharmony_ci	struct clk *clk;
1028c2ecf20Sopenharmony_ci	struct clk_init_data init;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	/* allocate the gate */
1058c2ecf20Sopenharmony_ci	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
1068c2ecf20Sopenharmony_ci	if (!gate)
1078c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	init.name = name;
1108c2ecf20Sopenharmony_ci	init.ops = &mmp_clk_gate_ops;
1118c2ecf20Sopenharmony_ci	init.flags = flags;
1128c2ecf20Sopenharmony_ci	init.parent_names = (parent_name ? &parent_name : NULL);
1138c2ecf20Sopenharmony_ci	init.num_parents = (parent_name ? 1 : 0);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	/* struct clk_gate assignments */
1168c2ecf20Sopenharmony_ci	gate->reg = reg;
1178c2ecf20Sopenharmony_ci	gate->mask = mask;
1188c2ecf20Sopenharmony_ci	gate->val_enable = val_enable;
1198c2ecf20Sopenharmony_ci	gate->val_disable = val_disable;
1208c2ecf20Sopenharmony_ci	gate->flags = gate_flags;
1218c2ecf20Sopenharmony_ci	gate->lock = lock;
1228c2ecf20Sopenharmony_ci	gate->hw.init = &init;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	clk = clk_register(dev, &gate->hw);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	if (IS_ERR(clk))
1278c2ecf20Sopenharmony_ci		kfree(gate);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	return clk;
1308c2ecf20Sopenharmony_ci}
131