1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * TI multiplexer clock support 4 * 5 * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it> 6 * 7 * Based on Linux kernel drivers/clk/ti/mux.c 8 */ 9 10#include <common.h> 11#include <dm.h> 12#include <dm/device_compat.h> 13#include <clk-uclass.h> 14#include <asm/io.h> 15#include <linux/clk-provider.h> 16#include "clk.h" 17 18struct clk_ti_mux_priv { 19 struct clk_bulk parents; 20 struct clk_ti_reg reg; 21 u32 flags; 22 u32 mux_flags; 23 u32 mask; 24 u32 shift; 25 s32 latch; 26}; 27 28static struct clk *clk_ti_mux_get_parent_by_index(struct clk_bulk *parents, 29 int index) 30{ 31 if (index < 0 || !parents) 32 return ERR_PTR(-EINVAL); 33 34 if (index >= parents->count) 35 return ERR_PTR(-ENODEV); 36 37 return &parents->clks[index]; 38} 39 40static int clk_ti_mux_get_parent_index(struct clk_bulk *parents, 41 struct clk *parent) 42{ 43 int i; 44 45 if (!parents || !parent) 46 return -EINVAL; 47 48 for (i = 0; i < parents->count; i++) { 49 if (parents->clks[i].dev == parent->dev) 50 return i; 51 } 52 53 return -ENODEV; 54} 55 56static int clk_ti_mux_get_index(struct clk *clk) 57{ 58 struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev); 59 u32 val; 60 61 val = clk_ti_readl(&priv->reg); 62 val >>= priv->shift; 63 val &= priv->mask; 64 65 if (val && (priv->flags & CLK_MUX_INDEX_BIT)) 66 val = ffs(val) - 1; 67 68 if (val && (priv->flags & CLK_MUX_INDEX_ONE)) 69 val--; 70 71 if (val >= priv->parents.count) 72 return -EINVAL; 73 74 return val; 75} 76 77static int clk_ti_mux_set_parent(struct clk *clk, struct clk *parent) 78{ 79 struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev); 80 int index; 81 u32 val; 82 83 index = clk_ti_mux_get_parent_index(&priv->parents, parent); 84 if (index < 0) { 85 dev_err(clk->dev, "failed to get parent clock\n"); 86 return index; 87 } 88 89 index = clk_mux_index_to_val(NULL, priv->flags, index); 90 91 if (priv->flags & CLK_MUX_HIWORD_MASK) { 92 val = priv->mask << (priv->shift + 16); 93 } else { 94 val = clk_ti_readl(&priv->reg); 95 val &= ~(priv->mask << priv->shift); 96 } 97 98 val |= index << priv->shift; 99 clk_ti_writel(val, &priv->reg); 100 clk_ti_latch(&priv->reg, priv->latch); 101 return 0; 102} 103 104static ulong clk_ti_mux_set_rate(struct clk *clk, ulong rate) 105{ 106 struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev); 107 struct clk *parent; 108 int index; 109 110 if ((clk->flags & CLK_SET_RATE_PARENT) == 0) 111 return -ENOSYS; 112 113 index = clk_ti_mux_get_index(clk); 114 parent = clk_ti_mux_get_parent_by_index(&priv->parents, index); 115 if (IS_ERR(parent)) 116 return PTR_ERR(parent); 117 118 rate = clk_set_rate(parent, rate); 119 dev_dbg(clk->dev, "rate=%ld\n", rate); 120 return rate; 121} 122 123static ulong clk_ti_mux_get_rate(struct clk *clk) 124{ 125 struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev); 126 int index; 127 struct clk *parent; 128 ulong rate; 129 130 index = clk_ti_mux_get_index(clk); 131 parent = clk_ti_mux_get_parent_by_index(&priv->parents, index); 132 if (IS_ERR(parent)) 133 return PTR_ERR(parent); 134 135 rate = clk_get_rate(parent); 136 dev_dbg(clk->dev, "rate=%ld\n", rate); 137 return rate; 138} 139 140static ulong clk_ti_mux_round_rate(struct clk *clk, ulong rate) 141{ 142 struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev); 143 struct clk *parent; 144 int index; 145 146 if ((clk->flags & CLK_SET_RATE_PARENT) == 0) 147 return -ENOSYS; 148 149 index = clk_ti_mux_get_index(clk); 150 parent = clk_ti_mux_get_parent_by_index(&priv->parents, index); 151 if (IS_ERR(parent)) 152 return PTR_ERR(parent); 153 154 rate = clk_round_rate(parent, rate); 155 dev_dbg(clk->dev, "rate=%ld\n", rate); 156 return rate; 157} 158 159static int clk_ti_mux_request(struct clk *clk) 160{ 161 struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev); 162 struct clk *parent; 163 int index; 164 165 clk->flags = priv->flags; 166 167 index = clk_ti_mux_get_index(clk); 168 parent = clk_ti_mux_get_parent_by_index(&priv->parents, index); 169 if (IS_ERR(parent)) 170 return PTR_ERR(parent); 171 172 return clk_ti_mux_set_parent(clk, parent); 173} 174 175static struct clk_ops clk_ti_mux_ops = { 176 .request = clk_ti_mux_request, 177 .round_rate = clk_ti_mux_round_rate, 178 .get_rate = clk_ti_mux_get_rate, 179 .set_rate = clk_ti_mux_set_rate, 180 .set_parent = clk_ti_mux_set_parent, 181}; 182 183static int clk_ti_mux_remove(struct udevice *dev) 184{ 185 struct clk_ti_mux_priv *priv = dev_get_priv(dev); 186 int err; 187 188 err = clk_release_all(priv->parents.clks, priv->parents.count); 189 if (err) 190 dev_dbg(dev, "could not release all parents' clocks\n"); 191 192 return err; 193} 194 195static int clk_ti_mux_probe(struct udevice *dev) 196{ 197 struct clk_ti_mux_priv *priv = dev_get_priv(dev); 198 int err; 199 200 err = clk_get_bulk(dev, &priv->parents); 201 if (err || priv->parents.count < 2) { 202 dev_err(dev, "mux-clock must have parents\n"); 203 return err ? err : -EFAULT; 204 } 205 206 /* Generate bit-mask based on parents info */ 207 priv->mask = priv->parents.count; 208 if (!(priv->mux_flags & CLK_MUX_INDEX_ONE)) 209 priv->mask--; 210 211 priv->mask = (1 << fls(priv->mask)) - 1; 212 return 0; 213} 214 215static int clk_ti_mux_of_to_plat(struct udevice *dev) 216{ 217 struct clk_ti_mux_priv *priv = dev_get_priv(dev); 218 int err; 219 220 err = clk_ti_get_reg_addr(dev, 0, &priv->reg); 221 if (err) { 222 dev_err(dev, "failed to get register address\n"); 223 return err; 224 } 225 226 priv->shift = dev_read_u32_default(dev, "ti,bit-shift", 0); 227 priv->latch = dev_read_s32_default(dev, "ti,latch-bit", -EINVAL); 228 229 priv->flags = CLK_SET_RATE_NO_REPARENT; 230 if (dev_read_bool(dev, "ti,set-rate-parent")) 231 priv->flags |= CLK_SET_RATE_PARENT; 232 233 if (dev_read_bool(dev, "ti,index-starts-at-one")) 234 priv->mux_flags |= CLK_MUX_INDEX_ONE; 235 236 return 0; 237} 238 239static const struct udevice_id clk_ti_mux_of_match[] = { 240 {.compatible = "ti,mux-clock"}, 241 {}, 242}; 243 244U_BOOT_DRIVER(clk_ti_mux) = { 245 .name = "ti_mux_clock", 246 .id = UCLASS_CLK, 247 .of_match = clk_ti_mux_of_match, 248 .of_to_plat = clk_ti_mux_of_to_plat, 249 .probe = clk_ti_mux_probe, 250 .remove = clk_ti_mux_remove, 251 .priv_auto = sizeof(struct clk_ti_mux_priv), 252 .ops = &clk_ti_mux_ops, 253}; 254