1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2016-2017 Socionext Inc. 4 * Author: Masahiro Yamada <yamada.masahiro@socionext.com> 5 */ 6 7 #include <common.h> 8 #include <clk-uclass.h> 9 #include <dm.h> 10 #include <linux/bitops.h> 11 #include <linux/io.h> 12 #include <linux/sizes.h> 13 14 #include "clk-uniphier.h" 15 16 /** 17 * struct uniphier_clk_priv - private data for UniPhier clock driver 18 * 19 * @base: base address of the clock provider 20 * @data: SoC specific data 21 */ 22 struct uniphier_clk_priv { 23 struct udevice *dev; 24 void __iomem *base; 25 const struct uniphier_clk_data *data; 26 }; 27 28 static void uniphier_clk_gate_enable(struct uniphier_clk_priv *priv, 29 const struct uniphier_clk_gate_data *gate) 30 { 31 u32 val; 32 33 val = readl(priv->base + gate->reg); 34 val |= BIT(gate->bit); 35 writel(val, priv->base + gate->reg); 36 } 37 38 static void uniphier_clk_mux_set_parent(struct uniphier_clk_priv *priv, 39 const struct uniphier_clk_mux_data *mux, 40 u8 id) 41 { 42 u32 val; 43 int i; 44 45 for (i = 0; i < mux->num_parents; i++) { 46 if (mux->parent_ids[i] != id) 47 continue; 48 49 val = readl(priv->base + mux->reg); 50 val &= ~mux->masks[i]; 51 val |= mux->vals[i]; 52 writel(val, priv->base + mux->reg); 53 return; 54 } 55 56 WARN_ON(1); 57 } 58 59 static u8 uniphier_clk_mux_get_parent(struct uniphier_clk_priv *priv, 60 const struct uniphier_clk_mux_data *mux) 61 { 62 u32 val; 63 int i; 64 65 val = readl(priv->base + mux->reg); 66 67 for (i = 0; i < mux->num_parents; i++) 68 if ((mux->masks[i] & val) == mux->vals[i]) 69 return mux->parent_ids[i]; 70 71 dev_err(priv->dev, "invalid mux setting\n"); 72 73 return UNIPHIER_CLK_ID_INVALID; 74 } 75 76 static const struct uniphier_clk_data *uniphier_clk_get_data( 77 struct uniphier_clk_priv *priv, u8 id) 78 { 79 const struct uniphier_clk_data *data; 80 81 for (data = priv->data; data->type != UNIPHIER_CLK_TYPE_END; data++) 82 if (data->id == id) 83 return data; 84 85 dev_err(priv->dev, "id=%u not found\n", id); 86 87 return NULL; 88 } 89 90 static const struct uniphier_clk_data *uniphier_clk_get_parent_data( 91 struct uniphier_clk_priv *priv, 92 const struct uniphier_clk_data *data) 93 { 94 const struct uniphier_clk_data *parent_data; 95 u8 parent_id = UNIPHIER_CLK_ID_INVALID; 96 97 switch (data->type) { 98 case UNIPHIER_CLK_TYPE_GATE: 99 parent_id = data->data.gate.parent_id; 100 break; 101 case UNIPHIER_CLK_TYPE_MUX: 102 parent_id = uniphier_clk_mux_get_parent(priv, &data->data.mux); 103 break; 104 default: 105 break; 106 } 107 108 if (parent_id == UNIPHIER_CLK_ID_INVALID) 109 return NULL; 110 111 parent_data = uniphier_clk_get_data(priv, parent_id); 112 113 WARN_ON(!parent_data); 114 115 return parent_data; 116 } 117 118 static void __uniphier_clk_enable(struct uniphier_clk_priv *priv, 119 const struct uniphier_clk_data *data) 120 { 121 const struct uniphier_clk_data *parent_data; 122 123 if (data->type == UNIPHIER_CLK_TYPE_GATE) 124 uniphier_clk_gate_enable(priv, &data->data.gate); 125 126 parent_data = uniphier_clk_get_parent_data(priv, data); 127 if (!parent_data) 128 return; 129 130 return __uniphier_clk_enable(priv, parent_data); 131 } 132 133 static int uniphier_clk_enable(struct clk *clk) 134 { 135 struct uniphier_clk_priv *priv = dev_get_priv(clk->dev); 136 const struct uniphier_clk_data *data; 137 138 data = uniphier_clk_get_data(priv, clk->id); 139 if (!data) 140 return -ENODEV; 141 142 __uniphier_clk_enable(priv, data); 143 144 return 0; 145 } 146 147 static unsigned long __uniphier_clk_get_rate( 148 struct uniphier_clk_priv *priv, 149 const struct uniphier_clk_data *data) 150 { 151 const struct uniphier_clk_data *parent_data; 152 153 if (data->type == UNIPHIER_CLK_TYPE_FIXED_RATE) 154 return data->data.rate.fixed_rate; 155 156 parent_data = uniphier_clk_get_parent_data(priv, data); 157 if (!parent_data) 158 return 0; 159 160 return __uniphier_clk_get_rate(priv, parent_data); 161 } 162 163 static unsigned long uniphier_clk_get_rate(struct clk *clk) 164 { 165 struct uniphier_clk_priv *priv = dev_get_priv(clk->dev); 166 const struct uniphier_clk_data *data; 167 168 data = uniphier_clk_get_data(priv, clk->id); 169 if (!data) 170 return -ENODEV; 171 172 return __uniphier_clk_get_rate(priv, data); 173 } 174 175 static unsigned long __uniphier_clk_set_rate( 176 struct uniphier_clk_priv *priv, 177 const struct uniphier_clk_data *data, 178 unsigned long rate, bool set) 179 { 180 const struct uniphier_clk_data *best_parent_data = NULL; 181 const struct uniphier_clk_data *parent_data; 182 unsigned long best_rate = 0; 183 unsigned long parent_rate; 184 u8 parent_id; 185 int i; 186 187 if (data->type == UNIPHIER_CLK_TYPE_FIXED_RATE) 188 return data->data.rate.fixed_rate; 189 190 if (data->type == UNIPHIER_CLK_TYPE_GATE) { 191 parent_data = uniphier_clk_get_parent_data(priv, data); 192 if (!parent_data) 193 return 0; 194 195 return __uniphier_clk_set_rate(priv, parent_data, rate, set); 196 } 197 198 if (WARN_ON(data->type != UNIPHIER_CLK_TYPE_MUX)) 199 return -EINVAL; 200 201 for (i = 0; i < data->data.mux.num_parents; i++) { 202 parent_id = data->data.mux.parent_ids[i]; 203 parent_data = uniphier_clk_get_data(priv, parent_id); 204 if (WARN_ON(!parent_data)) 205 return -EINVAL; 206 207 parent_rate = __uniphier_clk_set_rate(priv, parent_data, rate, 208 false); 209 210 if (parent_rate <= rate && best_rate < parent_rate) { 211 best_rate = parent_rate; 212 best_parent_data = parent_data; 213 } 214 } 215 216 dev_dbg(priv->dev, "id=%u, best_rate=%lu\n", data->id, best_rate); 217 218 if (!best_parent_data) 219 return -EINVAL; 220 221 if (!set) 222 return best_rate; 223 224 uniphier_clk_mux_set_parent(priv, &data->data.mux, 225 best_parent_data->id); 226 227 return best_rate = __uniphier_clk_set_rate(priv, best_parent_data, 228 rate, true); 229 } 230 231 static unsigned long uniphier_clk_set_rate(struct clk *clk, ulong rate) 232 { 233 struct uniphier_clk_priv *priv = dev_get_priv(clk->dev); 234 const struct uniphier_clk_data *data; 235 236 data = uniphier_clk_get_data(priv, clk->id); 237 if (!data) 238 return -ENODEV; 239 240 return __uniphier_clk_set_rate(priv, data, rate, true); 241 } 242 243 static const struct clk_ops uniphier_clk_ops = { 244 .enable = uniphier_clk_enable, 245 .get_rate = uniphier_clk_get_rate, 246 .set_rate = uniphier_clk_set_rate, 247 }; 248 249 static int uniphier_clk_probe(struct udevice *dev) 250 { 251 struct uniphier_clk_priv *priv = dev_get_priv(dev); 252 fdt_addr_t addr; 253 254 addr = devfdt_get_addr(dev->parent); 255 if (addr == FDT_ADDR_T_NONE) 256 return -EINVAL; 257 258 priv->base = devm_ioremap(dev, addr, SZ_4K); 259 if (!priv->base) 260 return -ENOMEM; 261 262 priv->dev = dev; 263 priv->data = (void *)dev_get_driver_data(dev); 264 265 return 0; 266 } 267 268 static const struct udevice_id uniphier_clk_match[] = { 269 /* System clock */ 270 { 271 .compatible = "socionext,uniphier-ld4-clock", 272 .data = (ulong)uniphier_pxs2_sys_clk_data, 273 }, 274 { 275 .compatible = "socionext,uniphier-pro4-clock", 276 .data = (ulong)uniphier_pxs2_sys_clk_data, 277 }, 278 { 279 .compatible = "socionext,uniphier-sld8-clock", 280 .data = (ulong)uniphier_pxs2_sys_clk_data, 281 }, 282 { 283 .compatible = "socionext,uniphier-pro5-clock", 284 .data = (ulong)uniphier_pxs2_sys_clk_data, 285 }, 286 { 287 .compatible = "socionext,uniphier-pxs2-clock", 288 .data = (ulong)uniphier_pxs2_sys_clk_data, 289 }, 290 { 291 .compatible = "socionext,uniphier-ld11-clock", 292 .data = (ulong)uniphier_ld20_sys_clk_data, 293 }, 294 { 295 .compatible = "socionext,uniphier-ld20-clock", 296 .data = (ulong)uniphier_ld20_sys_clk_data, 297 }, 298 { 299 .compatible = "socionext,uniphier-pxs3-clock", 300 .data = (ulong)uniphier_pxs3_sys_clk_data, 301 }, 302 /* Media I/O clock */ 303 { 304 .compatible = "socionext,uniphier-ld4-mio-clock", 305 .data = (ulong)uniphier_mio_clk_data, 306 }, 307 { 308 .compatible = "socionext,uniphier-pro4-mio-clock", 309 .data = (ulong)uniphier_mio_clk_data, 310 }, 311 { 312 .compatible = "socionext,uniphier-sld8-mio-clock", 313 .data = (ulong)uniphier_mio_clk_data, 314 }, 315 { 316 .compatible = "socionext,uniphier-pro5-sd-clock", 317 .data = (ulong)uniphier_mio_clk_data, 318 }, 319 { 320 .compatible = "socionext,uniphier-pxs2-sd-clock", 321 .data = (ulong)uniphier_mio_clk_data, 322 }, 323 { 324 .compatible = "socionext,uniphier-ld11-mio-clock", 325 .data = (ulong)uniphier_mio_clk_data, 326 }, 327 { 328 .compatible = "socionext,uniphier-ld20-sd-clock", 329 .data = (ulong)uniphier_mio_clk_data, 330 }, 331 { 332 .compatible = "socionext,uniphier-pxs3-sd-clock", 333 .data = (ulong)uniphier_mio_clk_data, 334 }, 335 { /* sentinel */ } 336 }; 337 338 U_BOOT_DRIVER(uniphier_clk) = { 339 .name = "uniphier-clk", 340 .id = UCLASS_CLK, 341 .of_match = uniphier_clk_match, 342 .probe = uniphier_clk_probe, 343 .priv_auto_alloc_size = sizeof(struct uniphier_clk_priv), 344 .ops = &uniphier_clk_ops, 345 }; 346