uboot/drivers/clk/altera/clk-arria10.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2018 Marek Vasut <marex@denx.de>
   4 */
   5
   6#include <common.h>
   7#include <malloc.h>
   8#include <asm/io.h>
   9#include <clk-uclass.h>
  10#include <dm.h>
  11#include <dm/device_compat.h>
  12#include <dm/devres.h>
  13#include <dm/lists.h>
  14#include <dm/util.h>
  15#include <linux/bitops.h>
  16#include <asm/global_data.h>
  17
  18#include <asm/arch/clock_manager.h>
  19
  20enum socfpga_a10_clk_type {
  21        SOCFPGA_A10_CLK_MAIN_PLL,
  22        SOCFPGA_A10_CLK_PER_PLL,
  23        SOCFPGA_A10_CLK_PERIP_CLK,
  24        SOCFPGA_A10_CLK_GATE_CLK,
  25        SOCFPGA_A10_CLK_UNKNOWN_CLK,
  26};
  27
  28struct socfpga_a10_clk_plat {
  29        enum socfpga_a10_clk_type type;
  30        struct clk_bulk clks;
  31        u32             regs;
  32        /* Fixed divider */
  33        u16             fix_div;
  34        /* Control register */
  35        u16             ctl_reg;
  36        /* Divider register */
  37        u16             div_reg;
  38        u8              div_len;
  39        u8              div_off;
  40        /* Clock gating register */
  41        u16             gate_reg;
  42        u8              gate_bit;
  43};
  44
  45static int socfpga_a10_clk_get_upstream(struct clk *clk, struct clk **upclk)
  46{
  47        struct socfpga_a10_clk_plat *plat = dev_get_plat(clk->dev);
  48        u32 reg, maxval;
  49
  50        if (plat->clks.count == 0)
  51                return 0;
  52
  53        if (plat->clks.count == 1) {
  54                *upclk = &plat->clks.clks[0];
  55                return 0;
  56        }
  57
  58        if (!plat->ctl_reg) {
  59                dev_err(clk->dev, "Invalid control register\n");
  60                return -EINVAL;
  61        }
  62
  63        reg = readl(plat->regs + plat->ctl_reg);
  64
  65        /* Assume PLLs are ON for now */
  66        if (plat->type == SOCFPGA_A10_CLK_MAIN_PLL) {
  67                reg = (reg >> 8) & 0x3;
  68                maxval = 2;
  69        } else if (plat->type == SOCFPGA_A10_CLK_PER_PLL) {
  70                reg = (reg >> 8) & 0x3;
  71                maxval = 3;
  72        } else {
  73                reg = (reg >> 16) & 0x7;
  74                maxval = 4;
  75        }
  76
  77        if (reg > maxval) {
  78                dev_err(clk->dev, "Invalid clock source\n");
  79                return -EINVAL;
  80        }
  81
  82        *upclk = &plat->clks.clks[reg];
  83        return 0;
  84}
  85
  86static int socfpga_a10_clk_endisable(struct clk *clk, bool enable)
  87{
  88        struct socfpga_a10_clk_plat *plat = dev_get_plat(clk->dev);
  89        struct clk *upclk = NULL;
  90        int ret;
  91
  92        if (!enable && plat->gate_reg)
  93                clrbits_le32(plat->regs + plat->gate_reg, BIT(plat->gate_bit));
  94
  95        ret = socfpga_a10_clk_get_upstream(clk, &upclk);
  96        if (ret)
  97                return ret;
  98
  99        if (upclk) {
 100                if (enable)
 101                        clk_enable(upclk);
 102                else
 103                        clk_disable(upclk);
 104        }
 105
 106        if (enable && plat->gate_reg)
 107                setbits_le32(plat->regs + plat->gate_reg, BIT(plat->gate_bit));
 108
 109        return 0;
 110}
 111
 112static int socfpga_a10_clk_enable(struct clk *clk)
 113{
 114        return socfpga_a10_clk_endisable(clk, true);
 115}
 116
 117static int socfpga_a10_clk_disable(struct clk *clk)
 118{
 119        return socfpga_a10_clk_endisable(clk, false);
 120}
 121
 122static ulong socfpga_a10_clk_get_rate(struct clk *clk)
 123{
 124        struct socfpga_a10_clk_plat *plat = dev_get_plat(clk->dev);
 125        struct clk *upclk = NULL;
 126        ulong rate = 0, reg, numer, denom;
 127        int ret;
 128
 129        ret = socfpga_a10_clk_get_upstream(clk, &upclk);
 130        if (ret || !upclk)
 131                return 0;
 132
 133        rate = clk_get_rate(upclk);
 134
 135        if (plat->type == SOCFPGA_A10_CLK_MAIN_PLL) {
 136                reg = readl(plat->regs + plat->ctl_reg + 4);    /* VCO1 */
 137                numer = reg & CLKMGR_MAINPLL_VCO1_NUMER_MSK;
 138                denom = (reg >> CLKMGR_MAINPLL_VCO1_DENOM_LSB) &
 139                        CLKMGR_MAINPLL_VCO1_DENOM_MSK;
 140
 141                rate /= denom + 1;
 142                rate *= numer + 1;
 143        } else if (plat->type == SOCFPGA_A10_CLK_PER_PLL) {
 144                reg = readl(plat->regs + plat->ctl_reg + 4);    /* VCO1 */
 145                numer = reg & CLKMGR_PERPLL_VCO1_NUMER_MSK;
 146                denom = (reg >> CLKMGR_PERPLL_VCO1_DENOM_LSB) &
 147                        CLKMGR_PERPLL_VCO1_DENOM_MSK;
 148
 149                rate /= denom + 1;
 150                rate *= numer + 1;
 151        } else {
 152                rate /= plat->fix_div;
 153
 154                if (plat->fix_div == 1 && plat->ctl_reg) {
 155                        reg = readl(plat->regs + plat->ctl_reg);
 156                        reg &= 0x7ff;
 157                        rate /= reg + 1;
 158                }
 159
 160                if (plat->div_reg) {
 161                        reg = readl(plat->regs + plat->div_reg);
 162                        reg >>= plat->div_off;
 163                        reg &= (1 << plat->div_len) - 1;
 164                        if (plat->type == SOCFPGA_A10_CLK_PERIP_CLK)
 165                                rate /= reg + 1;
 166                        if (plat->type == SOCFPGA_A10_CLK_GATE_CLK)
 167                                rate /= 1 << reg;
 168                }
 169        }
 170
 171        return rate;
 172}
 173
 174static struct clk_ops socfpga_a10_clk_ops = {
 175        .enable         = socfpga_a10_clk_enable,
 176        .disable        = socfpga_a10_clk_disable,
 177        .get_rate       = socfpga_a10_clk_get_rate,
 178};
 179
 180/*
 181 * This workaround tries to fix the massively broken generated "handoff" DT,
 182 * which contains duplicate clock nodes without any connection to the clock
 183 * manager DT node. Yet, those "handoff" DT nodes contain configuration of
 184 * the fixed input clock of the Arria10 which are missing from the base DT
 185 * for Arria10.
 186 *
 187 * This workaround sets up upstream clock for the fixed input clocks of the
 188 * A10 described in the base DT such that they map to the fixed clock from
 189 * the "handoff" DT. This does not fully match how the clock look on the
 190 * A10, but it is the least intrusive way to fix this mess.
 191 */
 192static void socfpga_a10_handoff_workaround(struct udevice *dev)
 193{
 194        struct socfpga_a10_clk_plat *plat = dev_get_plat(dev);
 195        const void *fdt = gd->fdt_blob;
 196        struct clk_bulk *bulk = &plat->clks;
 197        int i, ret, offset = dev_of_offset(dev);
 198        static const char * const socfpga_a10_fixedclk_map[] = {
 199                "osc1", "altera_arria10_hps_eosc1",
 200                "cb_intosc_ls_clk", "altera_arria10_hps_cb_intosc_ls",
 201                "f2s_free_clk", "altera_arria10_hps_f2h_free",
 202        };
 203
 204        if (fdt_node_check_compatible(fdt, offset, "fixed-clock"))
 205                return;
 206
 207        for (i = 0; i < ARRAY_SIZE(socfpga_a10_fixedclk_map); i += 2)
 208                if (!strcmp(dev->name, socfpga_a10_fixedclk_map[i]))
 209                        break;
 210
 211        if (i == ARRAY_SIZE(socfpga_a10_fixedclk_map))
 212                return;
 213
 214        ret = uclass_get_device_by_name(UCLASS_CLK,
 215                                        socfpga_a10_fixedclk_map[i + 1], &dev);
 216        if (ret)
 217                return;
 218
 219        bulk->count = 1;
 220        bulk->clks = devm_kcalloc(dev, bulk->count,
 221                                  sizeof(struct clk), GFP_KERNEL);
 222        if (!bulk->clks)
 223                return;
 224
 225        ret = clk_request(dev, &bulk->clks[0]);
 226        if (ret)
 227                free(bulk->clks);
 228}
 229
 230static int socfpga_a10_clk_bind(struct udevice *dev)
 231{
 232        const void *fdt = gd->fdt_blob;
 233        int offset = dev_of_offset(dev);
 234        bool pre_reloc_only = !(gd->flags & GD_FLG_RELOC);
 235        const char *name;
 236        int ret;
 237
 238        for (offset = fdt_first_subnode(fdt, offset);
 239             offset > 0;
 240             offset = fdt_next_subnode(fdt, offset)) {
 241                name = fdt_get_name(fdt, offset, NULL);
 242                if (!name)
 243                        return -EINVAL;
 244
 245                if (!strcmp(name, "clocks")) {
 246                        offset = fdt_first_subnode(fdt, offset);
 247                        name = fdt_get_name(fdt, offset, NULL);
 248                        if (!name)
 249                                return -EINVAL;
 250                }
 251
 252                /* Filter out supported sub-clock */
 253                if (fdt_node_check_compatible(fdt, offset,
 254                                              "altr,socfpga-a10-pll-clock") &&
 255                    fdt_node_check_compatible(fdt, offset,
 256                                              "altr,socfpga-a10-perip-clk") &&
 257                    fdt_node_check_compatible(fdt, offset,
 258                                              "altr,socfpga-a10-gate-clk") &&
 259                    fdt_node_check_compatible(fdt, offset, "fixed-clock"))
 260                        continue;
 261
 262                if (pre_reloc_only &&
 263                    !ofnode_pre_reloc(offset_to_ofnode(offset)))
 264                        continue;
 265
 266                ret = device_bind_driver_to_node(dev, "clk-a10", name,
 267                                                 offset_to_ofnode(offset),
 268                                                 NULL);
 269                if (ret)
 270                        return ret;
 271        }
 272
 273        return 0;
 274}
 275
 276static int socfpga_a10_clk_probe(struct udevice *dev)
 277{
 278        struct socfpga_a10_clk_plat *plat = dev_get_plat(dev);
 279        struct socfpga_a10_clk_plat *pplat;
 280        struct udevice *pdev;
 281        const void *fdt = gd->fdt_blob;
 282        int offset = dev_of_offset(dev);
 283
 284        clk_get_bulk(dev, &plat->clks);
 285
 286        socfpga_a10_handoff_workaround(dev);
 287
 288        if (!fdt_node_check_compatible(fdt, offset, "altr,clk-mgr")) {
 289                plat->regs = dev_read_addr(dev);
 290        } else {
 291                pdev = dev_get_parent(dev);
 292                if (!pdev)
 293                        return -ENODEV;
 294
 295                pplat = dev_get_plat(pdev);
 296                if (!pplat)
 297                        return -EINVAL;
 298
 299                plat->ctl_reg = dev_read_u32_default(dev, "reg", 0x0);
 300                plat->regs = pplat->regs;
 301        }
 302
 303        if (!fdt_node_check_compatible(fdt, offset,
 304                                       "altr,socfpga-a10-pll-clock")) {
 305                /* Main PLL has 3 upstream clock */
 306                if (plat->clks.count == 3)
 307                        plat->type = SOCFPGA_A10_CLK_MAIN_PLL;
 308                else
 309                        plat->type = SOCFPGA_A10_CLK_PER_PLL;
 310        } else if (!fdt_node_check_compatible(fdt, offset,
 311                                              "altr,socfpga-a10-perip-clk")) {
 312                plat->type = SOCFPGA_A10_CLK_PERIP_CLK;
 313        } else if (!fdt_node_check_compatible(fdt, offset,
 314                                              "altr,socfpga-a10-gate-clk")) {
 315                plat->type = SOCFPGA_A10_CLK_GATE_CLK;
 316        } else {
 317                plat->type = SOCFPGA_A10_CLK_UNKNOWN_CLK;
 318        }
 319
 320        return 0;
 321}
 322
 323static int socfpga_a10_of_to_plat(struct udevice *dev)
 324{
 325        struct socfpga_a10_clk_plat *plat = dev_get_plat(dev);
 326        unsigned int divreg[3], gatereg[2];
 327        int ret;
 328
 329        plat->type = SOCFPGA_A10_CLK_UNKNOWN_CLK;
 330
 331        plat->fix_div = dev_read_u32_default(dev, "fixed-divider", 1);
 332
 333        ret = dev_read_u32_array(dev, "div-reg", divreg, ARRAY_SIZE(divreg));
 334        if (!ret) {
 335                plat->div_reg = divreg[0];
 336                plat->div_len = divreg[2];
 337                plat->div_off = divreg[1];
 338        }
 339
 340        ret = dev_read_u32_array(dev, "clk-gate", gatereg, ARRAY_SIZE(gatereg));
 341        if (!ret) {
 342                plat->gate_reg = gatereg[0];
 343                plat->gate_bit = gatereg[1];
 344        }
 345
 346        return 0;
 347}
 348
 349static const struct udevice_id socfpga_a10_clk_match[] = {
 350        { .compatible = "altr,clk-mgr" },
 351        {}
 352};
 353
 354U_BOOT_DRIVER(socfpga_a10_clk) = {
 355        .name           = "clk-a10",
 356        .id             = UCLASS_CLK,
 357        .of_match       = socfpga_a10_clk_match,
 358        .ops            = &socfpga_a10_clk_ops,
 359        .bind           = socfpga_a10_clk_bind,
 360        .probe          = socfpga_a10_clk_probe,
 361        .of_to_plat = socfpga_a10_of_to_plat,
 362
 363        .plat_auto      = sizeof(struct socfpga_a10_clk_plat),
 364};
 365