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/io.h> 29 #include <linux/slab.h> 30 31 #include "clk.h" 32 33 /* clock separated gate register offset */ 34 #define CLKGATE_SEPERATED_ENABLE 0x0 35 #define CLKGATE_SEPERATED_DISABLE 0x4 36 #define CLKGATE_SEPERATED_STATUS 0x8 37 38 struct clkgate_separated { 39 struct clk_hw hw; 40 void __iomem *enable; /* enable register */ 41 u8 bit_idx; /* bits in enable/disable register */ 42 u8 flags; 43 spinlock_t *lock; 44 }; 45 46 static int clkgate_separated_enable(struct clk_hw *hw) 47 { 48 struct clkgate_separated *sclk; 49 unsigned long flags = 0; 50 u32 reg; 51 52 sclk = container_of(hw, struct clkgate_separated, hw); 53 if (sclk->lock) 54 spin_lock_irqsave(sclk->lock, flags); 55 reg = BIT(sclk->bit_idx); 56 writel_relaxed(reg, sclk->enable); 57 readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS); 58 if (sclk->lock) 59 spin_unlock_irqrestore(sclk->lock, flags); 60 return 0; 61 } 62 63 static void clkgate_separated_disable(struct clk_hw *hw) 64 { 65 struct clkgate_separated *sclk; 66 unsigned long flags = 0; 67 u32 reg; 68 69 sclk = container_of(hw, struct clkgate_separated, hw); 70 if (sclk->lock) 71 spin_lock_irqsave(sclk->lock, flags); 72 reg = BIT(sclk->bit_idx); 73 writel_relaxed(reg, sclk->enable + CLKGATE_SEPERATED_DISABLE); 74 readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS); 75 if (sclk->lock) 76 spin_unlock_irqrestore(sclk->lock, flags); 77 } 78 79 static int clkgate_separated_is_enabled(struct clk_hw *hw) 80 { 81 struct clkgate_separated *sclk; 82 u32 reg; 83 84 sclk = container_of(hw, struct clkgate_separated, hw); 85 reg = readl_relaxed(sclk->enable + CLKGATE_SEPERATED_STATUS); 86 reg &= BIT(sclk->bit_idx); 87 88 return reg ? 1 : 0; 89 } 90 91 static const struct clk_ops clkgate_separated_ops = { 92 .enable = clkgate_separated_enable, 93 .disable = clkgate_separated_disable, 94 .is_enabled = clkgate_separated_is_enabled, 95 }; 96 97 struct clk *hisi_register_clkgate_sep(struct device *dev, const char *name, 98 const char *parent_name, 99 unsigned long flags, 100 void __iomem *reg, u8 bit_idx, 101 u8 clk_gate_flags, spinlock_t *lock) 102 { 103 struct clkgate_separated *sclk; 104 struct clk *clk; 105 struct clk_init_data init; 106 107 sclk = kzalloc(sizeof(*sclk), GFP_KERNEL); 108 if (!sclk) 109 return ERR_PTR(-ENOMEM); 110 111 init.name = name; 112 init.ops = &clkgate_separated_ops; 113 init.flags = flags | CLK_IS_BASIC; 114 init.parent_names = (parent_name ? &parent_name : NULL); 115 init.num_parents = (parent_name ? 1 : 0); 116 117 sclk->enable = reg + CLKGATE_SEPERATED_ENABLE; 118 sclk->bit_idx = bit_idx; 119 sclk->flags = clk_gate_flags; 120 sclk->hw.init = &init; 121 sclk->lock = lock; 122 123 clk = clk_register(dev, &sclk->hw); 124 if (IS_ERR(clk)) 125 kfree(sclk); 126 return clk; 127 } 128