1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright 2014 Freescale Semiconductor, Inc. 4 */ 5 6 #include <linux/clk-provider.h> 7 #include <linux/err.h> 8 #include <linux/io.h> 9 #include <linux/slab.h> 10 #include "clk.h" 11 12 /** 13 * struct clk_gate_exclusive - i.MX specific gate clock which is mutually 14 * exclusive with other gate clocks 15 * 16 * @gate: the parent class 17 * @exclusive_mask: mask of gate bits which are mutually exclusive to this 18 * gate clock 19 * 20 * The imx exclusive gate clock is a subclass of basic clk_gate 21 * with an addtional mask to indicate which other gate bits in the same 22 * register is mutually exclusive to this gate clock. 23 */ 24 struct clk_gate_exclusive { 25 struct clk_gate gate; 26 u32 exclusive_mask; 27 }; 28 29 static int clk_gate_exclusive_enable(struct clk_hw *hw) 30 { 31 struct clk_gate *gate = to_clk_gate(hw); 32 struct clk_gate_exclusive *exgate = container_of(gate, 33 struct clk_gate_exclusive, gate); 34 u32 val = readl(gate->reg); 35 36 if (val & exgate->exclusive_mask) 37 return -EBUSY; 38 39 return clk_gate_ops.enable(hw); 40 } 41 42 static void clk_gate_exclusive_disable(struct clk_hw *hw) 43 { 44 clk_gate_ops.disable(hw); 45 } 46 47 static int clk_gate_exclusive_is_enabled(struct clk_hw *hw) 48 { 49 return clk_gate_ops.is_enabled(hw); 50 } 51 52 static const struct clk_ops clk_gate_exclusive_ops = { 53 .enable = clk_gate_exclusive_enable, 54 .disable = clk_gate_exclusive_disable, 55 .is_enabled = clk_gate_exclusive_is_enabled, 56 }; 57 58 struct clk_hw *imx_clk_hw_gate_exclusive(const char *name, const char *parent, 59 void __iomem *reg, u8 shift, u32 exclusive_mask) 60 { 61 struct clk_gate_exclusive *exgate; 62 struct clk_gate *gate; 63 struct clk_hw *hw; 64 struct clk_init_data init; 65 int ret; 66 67 if (exclusive_mask == 0) 68 return ERR_PTR(-EINVAL); 69 70 exgate = kzalloc(sizeof(*exgate), GFP_KERNEL); 71 if (!exgate) 72 return ERR_PTR(-ENOMEM); 73 gate = &exgate->gate; 74 75 init.name = name; 76 init.ops = &clk_gate_exclusive_ops; 77 init.flags = CLK_SET_RATE_PARENT; 78 init.parent_names = parent ? &parent : NULL; 79 init.num_parents = parent ? 1 : 0; 80 81 gate->reg = reg; 82 gate->bit_idx = shift; 83 gate->lock = &imx_ccm_lock; 84 gate->hw.init = &init; 85 exgate->exclusive_mask = exclusive_mask; 86 87 hw = &gate->hw; 88 89 ret = clk_hw_register(NULL, hw); 90 if (ret) { 91 kfree(gate); 92 return ERR_PTR(ret); 93 } 94 95 return hw; 96 } 97