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