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