Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | // SPDX-License-Identifier: GPL-2.0 /* * Zynq UltraScale+ MPSoC mux * * Copyright (C) 2016-2018 Xilinx */ #include <linux/clk-provider.h> #include <linux/slab.h> #include "clk-zynqmp.h" /* * DOC: basic adjustable multiplexer clock that cannot gate * * Traits of this clock: * prepare - clk_prepare only ensures that parents are prepared * enable - clk_enable only ensures that parents are enabled * rate - rate is only affected by parent switching. No clk_set_rate support * parent - parent is adjustable through clk_set_parent */ /** * struct zynqmp_clk_mux - multiplexer clock * * @hw: handle between common and hardware-specific interfaces * @flags: hardware-specific flags * @clk_id: Id of clock */ struct zynqmp_clk_mux { struct clk_hw hw; u8 flags; u32 clk_id; }; #define to_zynqmp_clk_mux(_hw) container_of(_hw, struct zynqmp_clk_mux, hw) /** * zynqmp_clk_mux_get_parent() - Get parent of clock * @hw: handle between common and hardware-specific interfaces * * Return: Parent index */ static u8 zynqmp_clk_mux_get_parent(struct clk_hw *hw) { struct zynqmp_clk_mux *mux = to_zynqmp_clk_mux(hw); const char *clk_name = clk_hw_get_name(hw); u32 clk_id = mux->clk_id; u32 val; int ret; const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); ret = eemi_ops->clock_getparent(clk_id, &val); if (ret) pr_warn_once("%s() getparent failed for clock: %s, ret = %d\n", __func__, clk_name, ret); return val; } /** * zynqmp_clk_mux_set_parent() - Set parent of clock * @hw: handle between common and hardware-specific interfaces * @index: Parent index * * Return: 0 on success else error+reason */ static int zynqmp_clk_mux_set_parent(struct clk_hw *hw, u8 index) { struct zynqmp_clk_mux *mux = to_zynqmp_clk_mux(hw); const char *clk_name = clk_hw_get_name(hw); u32 clk_id = mux->clk_id; int ret; const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); ret = eemi_ops->clock_setparent(clk_id, index); if (ret) pr_warn_once("%s() set parent failed for clock: %s, ret = %d\n", __func__, clk_name, ret); return ret; } static const struct clk_ops zynqmp_clk_mux_ops = { .get_parent = zynqmp_clk_mux_get_parent, .set_parent = zynqmp_clk_mux_set_parent, .determine_rate = __clk_mux_determine_rate, }; static const struct clk_ops zynqmp_clk_mux_ro_ops = { .get_parent = zynqmp_clk_mux_get_parent, }; /** * zynqmp_clk_register_mux() - Register a mux table with the clock * framework * @name: Name of this clock * @clk_id: Id of this clock * @parents: Name of this clock's parents * @num_parents: Number of parents * @nodes: Clock topology node * * Return: clock hardware of the registered clock mux */ struct clk_hw *zynqmp_clk_register_mux(const char *name, u32 clk_id, const char * const *parents, u8 num_parents, const struct clock_topology *nodes) { struct zynqmp_clk_mux *mux; struct clk_hw *hw; struct clk_init_data init; int ret; mux = kzalloc(sizeof(*mux), GFP_KERNEL); if (!mux) return ERR_PTR(-ENOMEM); init.name = name; if (nodes->type_flag & CLK_MUX_READ_ONLY) init.ops = &zynqmp_clk_mux_ro_ops; else init.ops = &zynqmp_clk_mux_ops; init.flags = nodes->flag; init.parent_names = parents; init.num_parents = num_parents; mux->flags = nodes->type_flag; mux->hw.init = &init; mux->clk_id = clk_id; hw = &mux->hw; ret = clk_hw_register(NULL, hw); if (ret) { kfree(hw); hw = ERR_PTR(ret); } return hw; } |