uboot/drivers/clk/uniphier/clk-uniphier-core.c
<<
>>
Prefs
   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 */
  22struct uniphier_clk_priv {
  23        struct udevice *dev;
  24        void __iomem *base;
  25        const struct uniphier_clk_data *data;
  26};
  27
  28static 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
  38static 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
  59static 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
  76static 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
  90static 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
 118static 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
 133static 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
 147static 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
 163static 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
 175static 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
 231static 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
 243static 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
 249static 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
 268static 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
 338U_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