linux/drivers/clk/sifive/fu540-prci.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2018-2019 SiFive, Inc.
   4 * Wesley Terpstra
   5 * Paul Walmsley
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 * The FU540 PRCI implements clock and reset control for the SiFive
  17 * FU540-C000 chip.  This driver assumes that it has sole control
  18 * over all PRCI resources.
  19 *
  20 * This driver is based on the PRCI driver written by Wesley Terpstra:
  21 * https://github.com/riscv/riscv-linux/commit/999529edf517ed75b56659d456d221b2ee56bb60
  22 *
  23 * References:
  24 * - SiFive FU540-C000 manual v1p0, Chapter 7 "Clocking and Reset"
  25 */
  26
  27#include <dt-bindings/clock/sifive-fu540-prci.h>
  28#include <linux/clkdev.h>
  29#include <linux/clk-provider.h>
  30#include <linux/clk/analogbits-wrpll-cln28hpc.h>
  31#include <linux/delay.h>
  32#include <linux/err.h>
  33#include <linux/io.h>
  34#include <linux/module.h>
  35#include <linux/of.h>
  36#include <linux/of_clk.h>
  37#include <linux/platform_device.h>
  38#include <linux/slab.h>
  39
  40/*
  41 * EXPECTED_CLK_PARENT_COUNT: how many parent clocks this driver expects:
  42 *     hfclk and rtcclk
  43 */
  44#define EXPECTED_CLK_PARENT_COUNT               2
  45
  46/*
  47 * Register offsets and bitmasks
  48 */
  49
  50/* COREPLLCFG0 */
  51#define PRCI_COREPLLCFG0_OFFSET                 0x4
  52# define PRCI_COREPLLCFG0_DIVR_SHIFT            0
  53# define PRCI_COREPLLCFG0_DIVR_MASK             (0x3f << PRCI_COREPLLCFG0_DIVR_SHIFT)
  54# define PRCI_COREPLLCFG0_DIVF_SHIFT            6
  55# define PRCI_COREPLLCFG0_DIVF_MASK             (0x1ff << PRCI_COREPLLCFG0_DIVF_SHIFT)
  56# define PRCI_COREPLLCFG0_DIVQ_SHIFT            15
  57# define PRCI_COREPLLCFG0_DIVQ_MASK             (0x7 << PRCI_COREPLLCFG0_DIVQ_SHIFT)
  58# define PRCI_COREPLLCFG0_RANGE_SHIFT           18
  59# define PRCI_COREPLLCFG0_RANGE_MASK            (0x7 << PRCI_COREPLLCFG0_RANGE_SHIFT)
  60# define PRCI_COREPLLCFG0_BYPASS_SHIFT          24
  61# define PRCI_COREPLLCFG0_BYPASS_MASK           (0x1 << PRCI_COREPLLCFG0_BYPASS_SHIFT)
  62# define PRCI_COREPLLCFG0_FSE_SHIFT             25
  63# define PRCI_COREPLLCFG0_FSE_MASK              (0x1 << PRCI_COREPLLCFG0_FSE_SHIFT)
  64# define PRCI_COREPLLCFG0_LOCK_SHIFT            31
  65# define PRCI_COREPLLCFG0_LOCK_MASK             (0x1 << PRCI_COREPLLCFG0_LOCK_SHIFT)
  66
  67/* DDRPLLCFG0 */
  68#define PRCI_DDRPLLCFG0_OFFSET                  0xc
  69# define PRCI_DDRPLLCFG0_DIVR_SHIFT             0
  70# define PRCI_DDRPLLCFG0_DIVR_MASK              (0x3f << PRCI_DDRPLLCFG0_DIVR_SHIFT)
  71# define PRCI_DDRPLLCFG0_DIVF_SHIFT             6
  72# define PRCI_DDRPLLCFG0_DIVF_MASK              (0x1ff << PRCI_DDRPLLCFG0_DIVF_SHIFT)
  73# define PRCI_DDRPLLCFG0_DIVQ_SHIFT             15
  74# define PRCI_DDRPLLCFG0_DIVQ_MASK              (0x7 << PRCI_DDRPLLCFG0_DIVQ_SHIFT)
  75# define PRCI_DDRPLLCFG0_RANGE_SHIFT            18
  76# define PRCI_DDRPLLCFG0_RANGE_MASK             (0x7 << PRCI_DDRPLLCFG0_RANGE_SHIFT)
  77# define PRCI_DDRPLLCFG0_BYPASS_SHIFT           24
  78# define PRCI_DDRPLLCFG0_BYPASS_MASK            (0x1 << PRCI_DDRPLLCFG0_BYPASS_SHIFT)
  79# define PRCI_DDRPLLCFG0_FSE_SHIFT              25
  80# define PRCI_DDRPLLCFG0_FSE_MASK               (0x1 << PRCI_DDRPLLCFG0_FSE_SHIFT)
  81# define PRCI_DDRPLLCFG0_LOCK_SHIFT             31
  82# define PRCI_DDRPLLCFG0_LOCK_MASK              (0x1 << PRCI_DDRPLLCFG0_LOCK_SHIFT)
  83
  84/* DDRPLLCFG1 */
  85#define PRCI_DDRPLLCFG1_OFFSET                  0x10
  86# define PRCI_DDRPLLCFG1_CKE_SHIFT              24
  87# define PRCI_DDRPLLCFG1_CKE_MASK               (0x1 << PRCI_DDRPLLCFG1_CKE_SHIFT)
  88
  89/* GEMGXLPLLCFG0 */
  90#define PRCI_GEMGXLPLLCFG0_OFFSET               0x1c
  91# define PRCI_GEMGXLPLLCFG0_DIVR_SHIFT          0
  92# define PRCI_GEMGXLPLLCFG0_DIVR_MASK           (0x3f << PRCI_GEMGXLPLLCFG0_DIVR_SHIFT)
  93# define PRCI_GEMGXLPLLCFG0_DIVF_SHIFT          6
  94# define PRCI_GEMGXLPLLCFG0_DIVF_MASK           (0x1ff << PRCI_GEMGXLPLLCFG0_DIVF_SHIFT)
  95# define PRCI_GEMGXLPLLCFG0_DIVQ_SHIFT          15
  96# define PRCI_GEMGXLPLLCFG0_DIVQ_MASK           (0x7 << PRCI_GEMGXLPLLCFG0_DIVQ_SHIFT)
  97# define PRCI_GEMGXLPLLCFG0_RANGE_SHIFT         18
  98# define PRCI_GEMGXLPLLCFG0_RANGE_MASK          (0x7 << PRCI_GEMGXLPLLCFG0_RANGE_SHIFT)
  99# define PRCI_GEMGXLPLLCFG0_BYPASS_SHIFT        24
 100# define PRCI_GEMGXLPLLCFG0_BYPASS_MASK         (0x1 << PRCI_GEMGXLPLLCFG0_BYPASS_SHIFT)
 101# define PRCI_GEMGXLPLLCFG0_FSE_SHIFT           25
 102# define PRCI_GEMGXLPLLCFG0_FSE_MASK            (0x1 << PRCI_GEMGXLPLLCFG0_FSE_SHIFT)
 103# define PRCI_GEMGXLPLLCFG0_LOCK_SHIFT          31
 104# define PRCI_GEMGXLPLLCFG0_LOCK_MASK           (0x1 << PRCI_GEMGXLPLLCFG0_LOCK_SHIFT)
 105
 106/* GEMGXLPLLCFG1 */
 107#define PRCI_GEMGXLPLLCFG1_OFFSET               0x20
 108# define PRCI_GEMGXLPLLCFG1_CKE_SHIFT           24
 109# define PRCI_GEMGXLPLLCFG1_CKE_MASK            (0x1 << PRCI_GEMGXLPLLCFG1_CKE_SHIFT)
 110
 111/* CORECLKSEL */
 112#define PRCI_CORECLKSEL_OFFSET                  0x24
 113# define PRCI_CORECLKSEL_CORECLKSEL_SHIFT       0
 114# define PRCI_CORECLKSEL_CORECLKSEL_MASK        (0x1 << PRCI_CORECLKSEL_CORECLKSEL_SHIFT)
 115
 116/* DEVICESRESETREG */
 117#define PRCI_DEVICESRESETREG_OFFSET                     0x28
 118# define PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_SHIFT      0
 119# define PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_MASK       (0x1 << PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_SHIFT)
 120# define PRCI_DEVICESRESETREG_DDR_AXI_RST_N_SHIFT       1
 121# define PRCI_DEVICESRESETREG_DDR_AXI_RST_N_MASK        (0x1 << PRCI_DEVICESRESETREG_DDR_AXI_RST_N_SHIFT)
 122# define PRCI_DEVICESRESETREG_DDR_AHB_RST_N_SHIFT       2
 123# define PRCI_DEVICESRESETREG_DDR_AHB_RST_N_MASK        (0x1 << PRCI_DEVICESRESETREG_DDR_AHB_RST_N_SHIFT)
 124# define PRCI_DEVICESRESETREG_DDR_PHY_RST_N_SHIFT       3
 125# define PRCI_DEVICESRESETREG_DDR_PHY_RST_N_MASK        (0x1 << PRCI_DEVICESRESETREG_DDR_PHY_RST_N_SHIFT)
 126# define PRCI_DEVICESRESETREG_GEMGXL_RST_N_SHIFT        5
 127# define PRCI_DEVICESRESETREG_GEMGXL_RST_N_MASK         (0x1 << PRCI_DEVICESRESETREG_GEMGXL_RST_N_SHIFT)
 128
 129/* CLKMUXSTATUSREG */
 130#define PRCI_CLKMUXSTATUSREG_OFFSET                     0x2c
 131# define PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_SHIFT     1
 132# define PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_MASK      (0x1 << PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_SHIFT)
 133
 134/*
 135 * Private structures
 136 */
 137
 138/**
 139 * struct __prci_data - per-device-instance data
 140 * @va: base virtual address of the PRCI IP block
 141 * @hw_clks: encapsulates struct clk_hw records
 142 *
 143 * PRCI per-device instance data
 144 */
 145struct __prci_data {
 146        void __iomem *va;
 147        struct clk_hw_onecell_data hw_clks;
 148};
 149
 150/**
 151 * struct __prci_wrpll_data - WRPLL configuration and integration data
 152 * @c: WRPLL current configuration record
 153 * @enable_bypass: fn ptr to code to bypass the WRPLL (if applicable; else NULL)
 154 * @disable_bypass: fn ptr to code to not bypass the WRPLL (or NULL)
 155 * @cfg0_offs: WRPLL CFG0 register offset (in bytes) from the PRCI base address
 156 *
 157 * @enable_bypass and @disable_bypass are used for WRPLL instances
 158 * that contain a separate external glitchless clock mux downstream
 159 * from the PLL.  The WRPLL internal bypass mux is not glitchless.
 160 */
 161struct __prci_wrpll_data {
 162        struct wrpll_cfg c;
 163        void (*enable_bypass)(struct __prci_data *pd);
 164        void (*disable_bypass)(struct __prci_data *pd);
 165        u8 cfg0_offs;
 166};
 167
 168/**
 169 * struct __prci_clock - describes a clock device managed by PRCI
 170 * @name: user-readable clock name string - should match the manual
 171 * @parent_name: parent name for this clock
 172 * @ops: struct clk_ops for the Linux clock framework to use for control
 173 * @hw: Linux-private clock data
 174 * @pwd: WRPLL-specific data, associated with this clock (if not NULL)
 175 * @pd: PRCI-specific data associated with this clock (if not NULL)
 176 *
 177 * PRCI clock data.  Used by the PRCI driver to register PRCI-provided
 178 * clocks to the Linux clock infrastructure.
 179 */
 180struct __prci_clock {
 181        const char *name;
 182        const char *parent_name;
 183        const struct clk_ops *ops;
 184        struct clk_hw hw;
 185        struct __prci_wrpll_data *pwd;
 186        struct __prci_data *pd;
 187};
 188
 189#define clk_hw_to_prci_clock(pwd) container_of(pwd, struct __prci_clock, hw)
 190
 191/*
 192 * Private functions
 193 */
 194
 195/**
 196 * __prci_readl() - read from a PRCI register
 197 * @pd: PRCI context
 198 * @offs: register offset to read from (in bytes, from PRCI base address)
 199 *
 200 * Read the register located at offset @offs from the base virtual
 201 * address of the PRCI register target described by @pd, and return
 202 * the value to the caller.
 203 *
 204 * Context: Any context.
 205 *
 206 * Return: the contents of the register described by @pd and @offs.
 207 */
 208static u32 __prci_readl(struct __prci_data *pd, u32 offs)
 209{
 210        return readl_relaxed(pd->va + offs);
 211}
 212
 213static void __prci_writel(u32 v, u32 offs, struct __prci_data *pd)
 214{
 215        writel_relaxed(v, pd->va + offs);
 216}
 217
 218/* WRPLL-related private functions */
 219
 220/**
 221 * __prci_wrpll_unpack() - unpack WRPLL configuration registers into parameters
 222 * @c: ptr to a struct wrpll_cfg record to write config into
 223 * @r: value read from the PRCI PLL configuration register
 224 *
 225 * Given a value @r read from an FU540 PRCI PLL configuration register,
 226 * split it into fields and populate it into the WRPLL configuration record
 227 * pointed to by @c.
 228 *
 229 * The COREPLLCFG0 macros are used below, but the other *PLLCFG0 macros
 230 * have the same register layout.
 231 *
 232 * Context: Any context.
 233 */
 234static void __prci_wrpll_unpack(struct wrpll_cfg *c, u32 r)
 235{
 236        u32 v;
 237
 238        v = r & PRCI_COREPLLCFG0_DIVR_MASK;
 239        v >>= PRCI_COREPLLCFG0_DIVR_SHIFT;
 240        c->divr = v;
 241
 242        v = r & PRCI_COREPLLCFG0_DIVF_MASK;
 243        v >>= PRCI_COREPLLCFG0_DIVF_SHIFT;
 244        c->divf = v;
 245
 246        v = r & PRCI_COREPLLCFG0_DIVQ_MASK;
 247        v >>= PRCI_COREPLLCFG0_DIVQ_SHIFT;
 248        c->divq = v;
 249
 250        v = r & PRCI_COREPLLCFG0_RANGE_MASK;
 251        v >>= PRCI_COREPLLCFG0_RANGE_SHIFT;
 252        c->range = v;
 253
 254        c->flags &= (WRPLL_FLAGS_INT_FEEDBACK_MASK |
 255                     WRPLL_FLAGS_EXT_FEEDBACK_MASK);
 256
 257        /* external feedback mode not supported */
 258        c->flags |= WRPLL_FLAGS_INT_FEEDBACK_MASK;
 259}
 260
 261/**
 262 * __prci_wrpll_pack() - pack PLL configuration parameters into a register value
 263 * @c: pointer to a struct wrpll_cfg record containing the PLL's cfg
 264 *
 265 * Using a set of WRPLL configuration values pointed to by @c,
 266 * assemble a PRCI PLL configuration register value, and return it to
 267 * the caller.
 268 *
 269 * Context: Any context.  Caller must ensure that the contents of the
 270 *          record pointed to by @c do not change during the execution
 271 *          of this function.
 272 *
 273 * Returns: a value suitable for writing into a PRCI PLL configuration
 274 *          register
 275 */
 276static u32 __prci_wrpll_pack(const struct wrpll_cfg *c)
 277{
 278        u32 r = 0;
 279
 280        r |= c->divr << PRCI_COREPLLCFG0_DIVR_SHIFT;
 281        r |= c->divf << PRCI_COREPLLCFG0_DIVF_SHIFT;
 282        r |= c->divq << PRCI_COREPLLCFG0_DIVQ_SHIFT;
 283        r |= c->range << PRCI_COREPLLCFG0_RANGE_SHIFT;
 284
 285        /* external feedback mode not supported */
 286        r |= PRCI_COREPLLCFG0_FSE_MASK;
 287
 288        return r;
 289}
 290
 291/**
 292 * __prci_wrpll_read_cfg() - read the WRPLL configuration from the PRCI
 293 * @pd: PRCI context
 294 * @pwd: PRCI WRPLL metadata
 295 *
 296 * Read the current configuration of the PLL identified by @pwd from
 297 * the PRCI identified by @pd, and store it into the local configuration
 298 * cache in @pwd.
 299 *
 300 * Context: Any context.  Caller must prevent the records pointed to by
 301 *          @pd and @pwd from changing during execution.
 302 */
 303static void __prci_wrpll_read_cfg(struct __prci_data *pd,
 304                                  struct __prci_wrpll_data *pwd)
 305{
 306        __prci_wrpll_unpack(&pwd->c, __prci_readl(pd, pwd->cfg0_offs));
 307}
 308
 309/**
 310 * __prci_wrpll_write_cfg() - write WRPLL configuration into the PRCI
 311 * @pd: PRCI context
 312 * @pwd: PRCI WRPLL metadata
 313 * @c: WRPLL configuration record to write
 314 *
 315 * Write the WRPLL configuration described by @c into the WRPLL
 316 * configuration register identified by @pwd in the PRCI instance
 317 * described by @c.  Make a cached copy of the WRPLL's current
 318 * configuration so it can be used by other code.
 319 *
 320 * Context: Any context.  Caller must prevent the records pointed to by
 321 *          @pd and @pwd from changing during execution.
 322 */
 323static void __prci_wrpll_write_cfg(struct __prci_data *pd,
 324                                   struct __prci_wrpll_data *pwd,
 325                                   struct wrpll_cfg *c)
 326{
 327        __prci_writel(__prci_wrpll_pack(c), pwd->cfg0_offs, pd);
 328
 329        memcpy(&pwd->c, c, sizeof(*c));
 330}
 331
 332/* Core clock mux control */
 333
 334/**
 335 * __prci_coreclksel_use_hfclk() - switch the CORECLK mux to output HFCLK
 336 * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg
 337 *
 338 * Switch the CORECLK mux to the HFCLK input source; return once complete.
 339 *
 340 * Context: Any context.  Caller must prevent concurrent changes to the
 341 *          PRCI_CORECLKSEL_OFFSET register.
 342 */
 343static void __prci_coreclksel_use_hfclk(struct __prci_data *pd)
 344{
 345        u32 r;
 346
 347        r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET);
 348        r |= PRCI_CORECLKSEL_CORECLKSEL_MASK;
 349        __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd);
 350
 351        r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */
 352}
 353
 354/**
 355 * __prci_coreclksel_use_corepll() - switch the CORECLK mux to output COREPLL
 356 * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg
 357 *
 358 * Switch the CORECLK mux to the PLL output clock; return once complete.
 359 *
 360 * Context: Any context.  Caller must prevent concurrent changes to the
 361 *          PRCI_CORECLKSEL_OFFSET register.
 362 */
 363static void __prci_coreclksel_use_corepll(struct __prci_data *pd)
 364{
 365        u32 r;
 366
 367        r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET);
 368        r &= ~PRCI_CORECLKSEL_CORECLKSEL_MASK;
 369        __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd);
 370
 371        r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */
 372}
 373
 374/*
 375 * Linux clock framework integration
 376 *
 377 * See the Linux clock framework documentation for more information on
 378 * these functions.
 379 */
 380
 381static unsigned long sifive_fu540_prci_wrpll_recalc_rate(struct clk_hw *hw,
 382                                                         unsigned long parent_rate)
 383{
 384        struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
 385        struct __prci_wrpll_data *pwd = pc->pwd;
 386
 387        return wrpll_calc_output_rate(&pwd->c, parent_rate);
 388}
 389
 390static long sifive_fu540_prci_wrpll_round_rate(struct clk_hw *hw,
 391                                               unsigned long rate,
 392                                               unsigned long *parent_rate)
 393{
 394        struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
 395        struct __prci_wrpll_data *pwd = pc->pwd;
 396        struct wrpll_cfg c;
 397
 398        memcpy(&c, &pwd->c, sizeof(c));
 399
 400        wrpll_configure_for_rate(&c, rate, *parent_rate);
 401
 402        return wrpll_calc_output_rate(&c, *parent_rate);
 403}
 404
 405static int sifive_fu540_prci_wrpll_set_rate(struct clk_hw *hw,
 406                                            unsigned long rate,
 407                                            unsigned long parent_rate)
 408{
 409        struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
 410        struct __prci_wrpll_data *pwd = pc->pwd;
 411        struct __prci_data *pd = pc->pd;
 412        int r;
 413
 414        r = wrpll_configure_for_rate(&pwd->c, rate, parent_rate);
 415        if (r)
 416                return r;
 417
 418        if (pwd->enable_bypass)
 419                pwd->enable_bypass(pd);
 420
 421        __prci_wrpll_write_cfg(pd, pwd, &pwd->c);
 422
 423        udelay(wrpll_calc_max_lock_us(&pwd->c));
 424
 425        if (pwd->disable_bypass)
 426                pwd->disable_bypass(pd);
 427
 428        return 0;
 429}
 430
 431static const struct clk_ops sifive_fu540_prci_wrpll_clk_ops = {
 432        .set_rate = sifive_fu540_prci_wrpll_set_rate,
 433        .round_rate = sifive_fu540_prci_wrpll_round_rate,
 434        .recalc_rate = sifive_fu540_prci_wrpll_recalc_rate,
 435};
 436
 437static const struct clk_ops sifive_fu540_prci_wrpll_ro_clk_ops = {
 438        .recalc_rate = sifive_fu540_prci_wrpll_recalc_rate,
 439};
 440
 441/* TLCLKSEL clock integration */
 442
 443static unsigned long sifive_fu540_prci_tlclksel_recalc_rate(struct clk_hw *hw,
 444                                                            unsigned long parent_rate)
 445{
 446        struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
 447        struct __prci_data *pd = pc->pd;
 448        u32 v;
 449        u8 div;
 450
 451        v = __prci_readl(pd, PRCI_CLKMUXSTATUSREG_OFFSET);
 452        v &= PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_MASK;
 453        div = v ? 1 : 2;
 454
 455        return div_u64(parent_rate, div);
 456}
 457
 458static const struct clk_ops sifive_fu540_prci_tlclksel_clk_ops = {
 459        .recalc_rate = sifive_fu540_prci_tlclksel_recalc_rate,
 460};
 461
 462/*
 463 * PRCI integration data for each WRPLL instance
 464 */
 465
 466static struct __prci_wrpll_data __prci_corepll_data = {
 467        .cfg0_offs = PRCI_COREPLLCFG0_OFFSET,
 468        .enable_bypass = __prci_coreclksel_use_hfclk,
 469        .disable_bypass = __prci_coreclksel_use_corepll,
 470};
 471
 472static struct __prci_wrpll_data __prci_ddrpll_data = {
 473        .cfg0_offs = PRCI_DDRPLLCFG0_OFFSET,
 474};
 475
 476static struct __prci_wrpll_data __prci_gemgxlpll_data = {
 477        .cfg0_offs = PRCI_GEMGXLPLLCFG0_OFFSET,
 478};
 479
 480/*
 481 * List of clock controls provided by the PRCI
 482 */
 483
 484static struct __prci_clock __prci_init_clocks[] = {
 485        [PRCI_CLK_COREPLL] = {
 486                .name = "corepll",
 487                .parent_name = "hfclk",
 488                .ops = &sifive_fu540_prci_wrpll_clk_ops,
 489                .pwd = &__prci_corepll_data,
 490        },
 491        [PRCI_CLK_DDRPLL] = {
 492                .name = "ddrpll",
 493                .parent_name = "hfclk",
 494                .ops = &sifive_fu540_prci_wrpll_ro_clk_ops,
 495                .pwd = &__prci_ddrpll_data,
 496        },
 497        [PRCI_CLK_GEMGXLPLL] = {
 498                .name = "gemgxlpll",
 499                .parent_name = "hfclk",
 500                .ops = &sifive_fu540_prci_wrpll_clk_ops,
 501                .pwd = &__prci_gemgxlpll_data,
 502        },
 503        [PRCI_CLK_TLCLK] = {
 504                .name = "tlclk",
 505                .parent_name = "corepll",
 506                .ops = &sifive_fu540_prci_tlclksel_clk_ops,
 507        },
 508};
 509
 510/**
 511 * __prci_register_clocks() - register clock controls in the PRCI with Linux
 512 * @dev: Linux struct device *
 513 *
 514 * Register the list of clock controls described in __prci_init_plls[] with
 515 * the Linux clock framework.
 516 *
 517 * Return: 0 upon success or a negative error code upon failure.
 518 */
 519static int __prci_register_clocks(struct device *dev, struct __prci_data *pd)
 520{
 521        struct clk_init_data init = { };
 522        struct __prci_clock *pic;
 523        int parent_count, i, r;
 524
 525        parent_count = of_clk_get_parent_count(dev->of_node);
 526        if (parent_count != EXPECTED_CLK_PARENT_COUNT) {
 527                dev_err(dev, "expected only two parent clocks, found %d\n",
 528                        parent_count);
 529                return -EINVAL;
 530        }
 531
 532        /* Register PLLs */
 533        for (i = 0; i < ARRAY_SIZE(__prci_init_clocks); ++i) {
 534                pic = &__prci_init_clocks[i];
 535
 536                init.name = pic->name;
 537                init.parent_names = &pic->parent_name;
 538                init.num_parents = 1;
 539                init.ops = pic->ops;
 540                pic->hw.init = &init;
 541
 542                pic->pd = pd;
 543
 544                if (pic->pwd)
 545                        __prci_wrpll_read_cfg(pd, pic->pwd);
 546
 547                r = devm_clk_hw_register(dev, &pic->hw);
 548                if (r) {
 549                        dev_warn(dev, "Failed to register clock %s: %d\n",
 550                                 init.name, r);
 551                        return r;
 552                }
 553
 554                r = clk_hw_register_clkdev(&pic->hw, pic->name, dev_name(dev));
 555                if (r) {
 556                        dev_warn(dev, "Failed to register clkdev for %s: %d\n",
 557                                 init.name, r);
 558                        return r;
 559                }
 560
 561                pd->hw_clks.hws[i] = &pic->hw;
 562        }
 563
 564        pd->hw_clks.num = i;
 565
 566        r = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
 567                                        &pd->hw_clks);
 568        if (r) {
 569                dev_err(dev, "could not add hw_provider: %d\n", r);
 570                return r;
 571        }
 572
 573        return 0;
 574}
 575
 576/*
 577 * Linux device model integration
 578 *
 579 * See the Linux device model documentation for more information about
 580 * these functions.
 581 */
 582static int sifive_fu540_prci_probe(struct platform_device *pdev)
 583{
 584        struct device *dev = &pdev->dev;
 585        struct resource *res;
 586        struct __prci_data *pd;
 587        int r;
 588
 589        pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
 590        if (!pd)
 591                return -ENOMEM;
 592
 593        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 594        pd->va = devm_ioremap_resource(dev, res);
 595        if (IS_ERR(pd->va))
 596                return PTR_ERR(pd->va);
 597
 598        r = __prci_register_clocks(dev, pd);
 599        if (r) {
 600                dev_err(dev, "could not register clocks: %d\n", r);
 601                return r;
 602        }
 603
 604        dev_dbg(dev, "SiFive FU540 PRCI probed\n");
 605
 606        return 0;
 607}
 608
 609static const struct of_device_id sifive_fu540_prci_of_match[] = {
 610        { .compatible = "sifive,fu540-c000-prci", },
 611        {}
 612};
 613MODULE_DEVICE_TABLE(of, sifive_fu540_prci_of_match);
 614
 615static struct platform_driver sifive_fu540_prci_driver = {
 616        .driver = {
 617                .name = "sifive-fu540-prci",
 618                .of_match_table = sifive_fu540_prci_of_match,
 619        },
 620        .probe = sifive_fu540_prci_probe,
 621};
 622
 623static int __init sifive_fu540_prci_init(void)
 624{
 625        return platform_driver_register(&sifive_fu540_prci_driver);
 626}
 627core_initcall(sifive_fu540_prci_init);
 628