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