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 *imx_clk_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 *clk; 64 struct clk_init_data init; 65 66 if (exclusive_mask == 0) 67 return ERR_PTR(-EINVAL); 68 69 exgate = kzalloc(sizeof(*exgate), GFP_KERNEL); 70 if (!exgate) 71 return ERR_PTR(-ENOMEM); 72 gate = &exgate->gate; 73 74 init.name = name; 75 init.ops = &clk_gate_exclusive_ops; 76 init.flags = CLK_SET_RATE_PARENT; 77 init.parent_names = parent ? &parent : NULL; 78 init.num_parents = parent ? 1 : 0; 79 80 gate->reg = reg; 81 gate->bit_idx = shift; 82 gate->lock = &imx_ccm_lock; 83 gate->hw.init = &init; 84 exgate->exclusive_mask = exclusive_mask; 85 86 clk = clk_register(NULL, &gate->hw); 87 if (IS_ERR(clk)) 88 kfree(exgate); 89 90 return clk; 91 } 92