1/*
2 * Hisilicon clock separated gate driver
3 *
4 * Copyright (c) 2012-2013 Hisilicon Limited.
5 * Copyright (c) 2012-2013 Linaro Limited.
6 *
7 * Author: Haojian Zhuang <haojian.zhuang@linaro.org>
8 *	   Xin Li <li.xin@linaro.org>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 *
24 */
25
26#include <linux/kernel.h>
27#include <linux/clk-provider.h>
28#include <linux/clkdev.h>
29#include <linux/io.h>
30#include <linux/slab.h>
31#include <linux/clk.h>
32
33#include "clk.h"
34
35/* clock separated gate register offset */
36#define CLKGATE_SEPERATED_ENABLE		0x0
37#define CLKGATE_SEPERATED_DISABLE		0x4
38#define CLKGATE_SEPERATED_STATUS		0x8
39
40struct clkgate_separated {
41	struct clk_hw	hw;
42	void __iomem	*enable;	/* enable register */
43	u8		bit_idx;	/* bits in enable/disable register */
44	u8		flags;
45	spinlock_t	*lock;
46};
47
48static int clkgate_separated_enable(struct clk_hw *hw)
49{
50	struct clkgate_separated *sclk;
51	unsigned long flags = 0;
52	u32 reg;
53
54	sclk = container_of(hw, struct clkgate_separated, hw);
55	if (sclk->lock)
56		spin_lock_irqsave(sclk->lock, flags);
57	reg = BIT(sclk->bit_idx);
58	writel_relaxed(reg, sclk->enable);
59	readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS);
60	if (sclk->lock)
61		spin_unlock_irqrestore(sclk->lock, flags);
62	return 0;
63}
64
65static void clkgate_separated_disable(struct clk_hw *hw)
66{
67	struct clkgate_separated *sclk;
68	unsigned long flags = 0;
69	u32 reg;
70
71	sclk = container_of(hw, struct clkgate_separated, hw);
72	if (sclk->lock)
73		spin_lock_irqsave(sclk->lock, flags);
74	reg = BIT(sclk->bit_idx);
75	writel_relaxed(reg, sclk->enable + CLKGATE_SEPERATED_DISABLE);
76	readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS);
77	if (sclk->lock)
78		spin_unlock_irqrestore(sclk->lock, flags);
79}
80
81static int clkgate_separated_is_enabled(struct clk_hw *hw)
82{
83	struct clkgate_separated *sclk;
84	u32 reg;
85
86	sclk = container_of(hw, struct clkgate_separated, hw);
87	reg = readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS);
88	reg &= BIT(sclk->bit_idx);
89
90	return reg ? 1 : 0;
91}
92
93static struct clk_ops clkgate_separated_ops = {
94	.enable		= clkgate_separated_enable,
95	.disable	= clkgate_separated_disable,
96	.is_enabled	= clkgate_separated_is_enabled,
97};
98
99struct clk *hisi_register_clkgate_sep(struct device *dev, const char *name,
100				      const char *parent_name,
101				      unsigned long flags,
102				      void __iomem *reg, u8 bit_idx,
103				      u8 clk_gate_flags, spinlock_t *lock)
104{
105	struct clkgate_separated *sclk;
106	struct clk *clk;
107	struct clk_init_data init;
108
109	sclk = kzalloc(sizeof(*sclk), GFP_KERNEL);
110	if (!sclk) {
111		pr_err("%s: fail to allocate separated gated clk\n", __func__);
112		return ERR_PTR(-ENOMEM);
113	}
114
115	init.name = name;
116	init.ops = &clkgate_separated_ops;
117	init.flags = flags | CLK_IS_BASIC;
118	init.parent_names = (parent_name ? &parent_name : NULL);
119	init.num_parents = (parent_name ? 1 : 0);
120
121	sclk->enable = reg + CLKGATE_SEPERATED_ENABLE;
122	sclk->bit_idx = bit_idx;
123	sclk->flags = clk_gate_flags;
124	sclk->hw.init = &init;
125
126	clk = clk_register(dev, &sclk->hw);
127	if (IS_ERR(clk))
128		kfree(sclk);
129	return clk;
130}
131