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 40 struct 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 48 static 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 65 static 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 81 static 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 93 static 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 99 struct 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