linux/drivers/clk/rockchip/clk.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (c) 2014 MundoReader S.L.
   4 * Author: Heiko Stuebner <heiko@sntech.de>
   5 *
   6 * Copyright (c) 2016 Rockchip Electronics Co. Ltd.
   7 * Author: Xing Zheng <zhengxing@rock-chips.com>
   8 *
   9 * based on
  10 *
  11 * samsung/clk.c
  12 * Copyright (c) 2013 Samsung Electronics Co., Ltd.
  13 * Copyright (c) 2013 Linaro Ltd.
  14 * Author: Thomas Abraham <thomas.ab@samsung.com>
  15 */
  16
  17#include <linux/slab.h>
  18#include <linux/clk.h>
  19#include <linux/clk-provider.h>
  20#include <linux/io.h>
  21#include <linux/mfd/syscon.h>
  22#include <linux/regmap.h>
  23#include <linux/reboot.h>
  24#include <linux/rational.h>
  25
  26#include "../clk-fractional-divider.h"
  27#include "clk.h"
  28
  29/*
  30 * Register a clock branch.
  31 * Most clock branches have a form like
  32 *
  33 * src1 --|--\
  34 *        |M |--[GATE]-[DIV]-
  35 * src2 --|--/
  36 *
  37 * sometimes without one of those components.
  38 */
  39static struct clk *rockchip_clk_register_branch(const char *name,
  40                const char *const *parent_names, u8 num_parents,
  41                void __iomem *base,
  42                int muxdiv_offset, u8 mux_shift, u8 mux_width, u8 mux_flags,
  43                int div_offset, u8 div_shift, u8 div_width, u8 div_flags,
  44                struct clk_div_table *div_table, int gate_offset,
  45                u8 gate_shift, u8 gate_flags, unsigned long flags,
  46                spinlock_t *lock)
  47{
  48        struct clk_hw *hw;
  49        struct clk_mux *mux = NULL;
  50        struct clk_gate *gate = NULL;
  51        struct clk_divider *div = NULL;
  52        const struct clk_ops *mux_ops = NULL, *div_ops = NULL,
  53                             *gate_ops = NULL;
  54        int ret;
  55
  56        if (num_parents > 1) {
  57                mux = kzalloc(sizeof(*mux), GFP_KERNEL);
  58                if (!mux)
  59                        return ERR_PTR(-ENOMEM);
  60
  61                mux->reg = base + muxdiv_offset;
  62                mux->shift = mux_shift;
  63                mux->mask = BIT(mux_width) - 1;
  64                mux->flags = mux_flags;
  65                mux->lock = lock;
  66                mux_ops = (mux_flags & CLK_MUX_READ_ONLY) ? &clk_mux_ro_ops
  67                                                        : &clk_mux_ops;
  68        }
  69
  70        if (gate_offset >= 0) {
  71                gate = kzalloc(sizeof(*gate), GFP_KERNEL);
  72                if (!gate) {
  73                        ret = -ENOMEM;
  74                        goto err_gate;
  75                }
  76
  77                gate->flags = gate_flags;
  78                gate->reg = base + gate_offset;
  79                gate->bit_idx = gate_shift;
  80                gate->lock = lock;
  81                gate_ops = &clk_gate_ops;
  82        }
  83
  84        if (div_width > 0) {
  85                div = kzalloc(sizeof(*div), GFP_KERNEL);
  86                if (!div) {
  87                        ret = -ENOMEM;
  88                        goto err_div;
  89                }
  90
  91                div->flags = div_flags;
  92                if (div_offset)
  93                        div->reg = base + div_offset;
  94                else
  95                        div->reg = base + muxdiv_offset;
  96                div->shift = div_shift;
  97                div->width = div_width;
  98                div->lock = lock;
  99                div->table = div_table;
 100                div_ops = (div_flags & CLK_DIVIDER_READ_ONLY)
 101                                                ? &clk_divider_ro_ops
 102                                                : &clk_divider_ops;
 103        }
 104
 105        hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
 106                                       mux ? &mux->hw : NULL, mux_ops,
 107                                       div ? &div->hw : NULL, div_ops,
 108                                       gate ? &gate->hw : NULL, gate_ops,
 109                                       flags);
 110        if (IS_ERR(hw)) {
 111                kfree(div);
 112                kfree(gate);
 113                return ERR_CAST(hw);
 114        }
 115
 116        return hw->clk;
 117err_div:
 118        kfree(gate);
 119err_gate:
 120        kfree(mux);
 121        return ERR_PTR(ret);
 122}
 123
 124struct rockchip_clk_frac {
 125        struct notifier_block                   clk_nb;
 126        struct clk_fractional_divider           div;
 127        struct clk_gate                         gate;
 128
 129        struct clk_mux                          mux;
 130        const struct clk_ops                    *mux_ops;
 131        int                                     mux_frac_idx;
 132
 133        bool                                    rate_change_remuxed;
 134        int                                     rate_change_idx;
 135};
 136
 137#define to_rockchip_clk_frac_nb(nb) \
 138                        container_of(nb, struct rockchip_clk_frac, clk_nb)
 139
 140static int rockchip_clk_frac_notifier_cb(struct notifier_block *nb,
 141                                         unsigned long event, void *data)
 142{
 143        struct clk_notifier_data *ndata = data;
 144        struct rockchip_clk_frac *frac = to_rockchip_clk_frac_nb(nb);
 145        struct clk_mux *frac_mux = &frac->mux;
 146        int ret = 0;
 147
 148        pr_debug("%s: event %lu, old_rate %lu, new_rate: %lu\n",
 149                 __func__, event, ndata->old_rate, ndata->new_rate);
 150        if (event == PRE_RATE_CHANGE) {
 151                frac->rate_change_idx =
 152                                frac->mux_ops->get_parent(&frac_mux->hw);
 153                if (frac->rate_change_idx != frac->mux_frac_idx) {
 154                        frac->mux_ops->set_parent(&frac_mux->hw,
 155                                                  frac->mux_frac_idx);
 156                        frac->rate_change_remuxed = 1;
 157                }
 158        } else if (event == POST_RATE_CHANGE) {
 159                /*
 160                 * The POST_RATE_CHANGE notifier runs directly after the
 161                 * divider clock is set in clk_change_rate, so we'll have
 162                 * remuxed back to the original parent before clk_change_rate
 163                 * reaches the mux itself.
 164                 */
 165                if (frac->rate_change_remuxed) {
 166                        frac->mux_ops->set_parent(&frac_mux->hw,
 167                                                  frac->rate_change_idx);
 168                        frac->rate_change_remuxed = 0;
 169                }
 170        }
 171
 172        return notifier_from_errno(ret);
 173}
 174
 175/*
 176 * fractional divider must set that denominator is 20 times larger than
 177 * numerator to generate precise clock frequency.
 178 */
 179static void rockchip_fractional_approximation(struct clk_hw *hw,
 180                unsigned long rate, unsigned long *parent_rate,
 181                unsigned long *m, unsigned long *n)
 182{
 183        struct clk_fractional_divider *fd = to_clk_fd(hw);
 184        unsigned long p_rate, p_parent_rate;
 185        struct clk_hw *p_parent;
 186
 187        p_rate = clk_hw_get_rate(clk_hw_get_parent(hw));
 188        if ((rate * 20 > p_rate) && (p_rate % rate != 0)) {
 189                p_parent = clk_hw_get_parent(clk_hw_get_parent(hw));
 190                p_parent_rate = clk_hw_get_rate(p_parent);
 191                *parent_rate = p_parent_rate;
 192        }
 193
 194        fd->flags |= CLK_FRAC_DIVIDER_POWER_OF_TWO_PS;
 195
 196        clk_fractional_divider_general_approximation(hw, rate, parent_rate, m, n);
 197}
 198
 199static struct clk *rockchip_clk_register_frac_branch(
 200                struct rockchip_clk_provider *ctx, const char *name,
 201                const char *const *parent_names, u8 num_parents,
 202                void __iomem *base, int muxdiv_offset, u8 div_flags,
 203                int gate_offset, u8 gate_shift, u8 gate_flags,
 204                unsigned long flags, struct rockchip_clk_branch *child,
 205                spinlock_t *lock)
 206{
 207        struct clk_hw *hw;
 208        struct rockchip_clk_frac *frac;
 209        struct clk_gate *gate = NULL;
 210        struct clk_fractional_divider *div = NULL;
 211        const struct clk_ops *div_ops = NULL, *gate_ops = NULL;
 212
 213        if (muxdiv_offset < 0)
 214                return ERR_PTR(-EINVAL);
 215
 216        if (child && child->branch_type != branch_mux) {
 217                pr_err("%s: fractional child clock for %s can only be a mux\n",
 218                       __func__, name);
 219                return ERR_PTR(-EINVAL);
 220        }
 221
 222        frac = kzalloc(sizeof(*frac), GFP_KERNEL);
 223        if (!frac)
 224                return ERR_PTR(-ENOMEM);
 225
 226        if (gate_offset >= 0) {
 227                gate = &frac->gate;
 228                gate->flags = gate_flags;
 229                gate->reg = base + gate_offset;
 230                gate->bit_idx = gate_shift;
 231                gate->lock = lock;
 232                gate_ops = &clk_gate_ops;
 233        }
 234
 235        div = &frac->div;
 236        div->flags = div_flags;
 237        div->reg = base + muxdiv_offset;
 238        div->mshift = 16;
 239        div->mwidth = 16;
 240        div->mmask = GENMASK(div->mwidth - 1, 0) << div->mshift;
 241        div->nshift = 0;
 242        div->nwidth = 16;
 243        div->nmask = GENMASK(div->nwidth - 1, 0) << div->nshift;
 244        div->lock = lock;
 245        div->approximation = rockchip_fractional_approximation;
 246        div_ops = &clk_fractional_divider_ops;
 247
 248        hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
 249                                       NULL, NULL,
 250                                       &div->hw, div_ops,
 251                                       gate ? &gate->hw : NULL, gate_ops,
 252                                       flags | CLK_SET_RATE_UNGATE);
 253        if (IS_ERR(hw)) {
 254                kfree(frac);
 255                return ERR_CAST(hw);
 256        }
 257
 258        if (child) {
 259                struct clk_mux *frac_mux = &frac->mux;
 260                struct clk_init_data init;
 261                struct clk *mux_clk;
 262                int ret;
 263
 264                frac->mux_frac_idx = match_string(child->parent_names,
 265                                                  child->num_parents, name);
 266                frac->mux_ops = &clk_mux_ops;
 267                frac->clk_nb.notifier_call = rockchip_clk_frac_notifier_cb;
 268
 269                frac_mux->reg = base + child->muxdiv_offset;
 270                frac_mux->shift = child->mux_shift;
 271                frac_mux->mask = BIT(child->mux_width) - 1;
 272                frac_mux->flags = child->mux_flags;
 273                frac_mux->lock = lock;
 274                frac_mux->hw.init = &init;
 275
 276                init.name = child->name;
 277                init.flags = child->flags | CLK_SET_RATE_PARENT;
 278                init.ops = frac->mux_ops;
 279                init.parent_names = child->parent_names;
 280                init.num_parents = child->num_parents;
 281
 282                mux_clk = clk_register(NULL, &frac_mux->hw);
 283                if (IS_ERR(mux_clk)) {
 284                        kfree(frac);
 285                        return mux_clk;
 286                }
 287
 288                rockchip_clk_add_lookup(ctx, mux_clk, child->id);
 289
 290                /* notifier on the fraction divider to catch rate changes */
 291                if (frac->mux_frac_idx >= 0) {
 292                        pr_debug("%s: found fractional parent in mux at pos %d\n",
 293                                 __func__, frac->mux_frac_idx);
 294                        ret = clk_notifier_register(hw->clk, &frac->clk_nb);
 295                        if (ret)
 296                                pr_err("%s: failed to register clock notifier for %s\n",
 297                                                __func__, name);
 298                } else {
 299                        pr_warn("%s: could not find %s as parent of %s, rate changes may not work\n",
 300                                __func__, name, child->name);
 301                }
 302        }
 303
 304        return hw->clk;
 305}
 306
 307static struct clk *rockchip_clk_register_factor_branch(const char *name,
 308                const char *const *parent_names, u8 num_parents,
 309                void __iomem *base, unsigned int mult, unsigned int div,
 310                int gate_offset, u8 gate_shift, u8 gate_flags,
 311                unsigned long flags, spinlock_t *lock)
 312{
 313        struct clk_hw *hw;
 314        struct clk_gate *gate = NULL;
 315        struct clk_fixed_factor *fix = NULL;
 316
 317        /* without gate, register a simple factor clock */
 318        if (gate_offset == 0) {
 319                return clk_register_fixed_factor(NULL, name,
 320                                parent_names[0], flags, mult,
 321                                div);
 322        }
 323
 324        gate = kzalloc(sizeof(*gate), GFP_KERNEL);
 325        if (!gate)
 326                return ERR_PTR(-ENOMEM);
 327
 328        gate->flags = gate_flags;
 329        gate->reg = base + gate_offset;
 330        gate->bit_idx = gate_shift;
 331        gate->lock = lock;
 332
 333        fix = kzalloc(sizeof(*fix), GFP_KERNEL);
 334        if (!fix) {
 335                kfree(gate);
 336                return ERR_PTR(-ENOMEM);
 337        }
 338
 339        fix->mult = mult;
 340        fix->div = div;
 341
 342        hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
 343                                       NULL, NULL,
 344                                       &fix->hw, &clk_fixed_factor_ops,
 345                                       &gate->hw, &clk_gate_ops, flags);
 346        if (IS_ERR(hw)) {
 347                kfree(fix);
 348                kfree(gate);
 349                return ERR_CAST(hw);
 350        }
 351
 352        return hw->clk;
 353}
 354
 355struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np,
 356                                                void __iomem *base,
 357                                                unsigned long nr_clks)
 358{
 359        struct rockchip_clk_provider *ctx;
 360        struct clk **clk_table;
 361        int i;
 362
 363        ctx = kzalloc(sizeof(struct rockchip_clk_provider), GFP_KERNEL);
 364        if (!ctx)
 365                return ERR_PTR(-ENOMEM);
 366
 367        clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL);
 368        if (!clk_table)
 369                goto err_free;
 370
 371        for (i = 0; i < nr_clks; ++i)
 372                clk_table[i] = ERR_PTR(-ENOENT);
 373
 374        ctx->reg_base = base;
 375        ctx->clk_data.clks = clk_table;
 376        ctx->clk_data.clk_num = nr_clks;
 377        ctx->cru_node = np;
 378        spin_lock_init(&ctx->lock);
 379
 380        ctx->grf = syscon_regmap_lookup_by_phandle(ctx->cru_node,
 381                                                   "rockchip,grf");
 382
 383        return ctx;
 384
 385err_free:
 386        kfree(ctx);
 387        return ERR_PTR(-ENOMEM);
 388}
 389EXPORT_SYMBOL_GPL(rockchip_clk_init);
 390
 391void rockchip_clk_of_add_provider(struct device_node *np,
 392                                  struct rockchip_clk_provider *ctx)
 393{
 394        if (of_clk_add_provider(np, of_clk_src_onecell_get,
 395                                &ctx->clk_data))
 396                pr_err("%s: could not register clk provider\n", __func__);
 397}
 398EXPORT_SYMBOL_GPL(rockchip_clk_of_add_provider);
 399
 400void rockchip_clk_add_lookup(struct rockchip_clk_provider *ctx,
 401                             struct clk *clk, unsigned int id)
 402{
 403        if (ctx->clk_data.clks && id)
 404                ctx->clk_data.clks[id] = clk;
 405}
 406EXPORT_SYMBOL_GPL(rockchip_clk_add_lookup);
 407
 408void rockchip_clk_register_plls(struct rockchip_clk_provider *ctx,
 409                                struct rockchip_pll_clock *list,
 410                                unsigned int nr_pll, int grf_lock_offset)
 411{
 412        struct clk *clk;
 413        int idx;
 414
 415        for (idx = 0; idx < nr_pll; idx++, list++) {
 416                clk = rockchip_clk_register_pll(ctx, list->type, list->name,
 417                                list->parent_names, list->num_parents,
 418                                list->con_offset, grf_lock_offset,
 419                                list->lock_shift, list->mode_offset,
 420                                list->mode_shift, list->rate_table,
 421                                list->flags, list->pll_flags);
 422                if (IS_ERR(clk)) {
 423                        pr_err("%s: failed to register clock %s\n", __func__,
 424                                list->name);
 425                        continue;
 426                }
 427
 428                rockchip_clk_add_lookup(ctx, clk, list->id);
 429        }
 430}
 431EXPORT_SYMBOL_GPL(rockchip_clk_register_plls);
 432
 433void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
 434                                    struct rockchip_clk_branch *list,
 435                                    unsigned int nr_clk)
 436{
 437        struct clk *clk = NULL;
 438        unsigned int idx;
 439        unsigned long flags;
 440
 441        for (idx = 0; idx < nr_clk; idx++, list++) {
 442                flags = list->flags;
 443
 444                /* catch simple muxes */
 445                switch (list->branch_type) {
 446                case branch_mux:
 447                        clk = clk_register_mux(NULL, list->name,
 448                                list->parent_names, list->num_parents,
 449                                flags, ctx->reg_base + list->muxdiv_offset,
 450                                list->mux_shift, list->mux_width,
 451                                list->mux_flags, &ctx->lock);
 452                        break;
 453                case branch_muxgrf:
 454                        clk = rockchip_clk_register_muxgrf(list->name,
 455                                list->parent_names, list->num_parents,
 456                                flags, ctx->grf, list->muxdiv_offset,
 457                                list->mux_shift, list->mux_width,
 458                                list->mux_flags);
 459                        break;
 460                case branch_divider:
 461                        if (list->div_table)
 462                                clk = clk_register_divider_table(NULL,
 463                                        list->name, list->parent_names[0],
 464                                        flags,
 465                                        ctx->reg_base + list->muxdiv_offset,
 466                                        list->div_shift, list->div_width,
 467                                        list->div_flags, list->div_table,
 468                                        &ctx->lock);
 469                        else
 470                                clk = clk_register_divider(NULL, list->name,
 471                                        list->parent_names[0], flags,
 472                                        ctx->reg_base + list->muxdiv_offset,
 473                                        list->div_shift, list->div_width,
 474                                        list->div_flags, &ctx->lock);
 475                        break;
 476                case branch_fraction_divider:
 477                        clk = rockchip_clk_register_frac_branch(ctx, list->name,
 478                                list->parent_names, list->num_parents,
 479                                ctx->reg_base, list->muxdiv_offset,
 480                                list->div_flags,
 481                                list->gate_offset, list->gate_shift,
 482                                list->gate_flags, flags, list->child,
 483                                &ctx->lock);
 484                        break;
 485                case branch_half_divider:
 486                        clk = rockchip_clk_register_halfdiv(list->name,
 487                                list->parent_names, list->num_parents,
 488                                ctx->reg_base, list->muxdiv_offset,
 489                                list->mux_shift, list->mux_width,
 490                                list->mux_flags, list->div_shift,
 491                                list->div_width, list->div_flags,
 492                                list->gate_offset, list->gate_shift,
 493                                list->gate_flags, flags, &ctx->lock);
 494                        break;
 495                case branch_gate:
 496                        flags |= CLK_SET_RATE_PARENT;
 497
 498                        clk = clk_register_gate(NULL, list->name,
 499                                list->parent_names[0], flags,
 500                                ctx->reg_base + list->gate_offset,
 501                                list->gate_shift, list->gate_flags, &ctx->lock);
 502                        break;
 503                case branch_composite:
 504                        clk = rockchip_clk_register_branch(list->name,
 505                                list->parent_names, list->num_parents,
 506                                ctx->reg_base, list->muxdiv_offset,
 507                                list->mux_shift,
 508                                list->mux_width, list->mux_flags,
 509                                list->div_offset, list->div_shift, list->div_width,
 510                                list->div_flags, list->div_table,
 511                                list->gate_offset, list->gate_shift,
 512                                list->gate_flags, flags, &ctx->lock);
 513                        break;
 514                case branch_mmc:
 515                        clk = rockchip_clk_register_mmc(
 516                                list->name,
 517                                list->parent_names, list->num_parents,
 518                                ctx->reg_base + list->muxdiv_offset,
 519                                list->div_shift
 520                        );
 521                        break;
 522                case branch_inverter:
 523                        clk = rockchip_clk_register_inverter(
 524                                list->name, list->parent_names,
 525                                list->num_parents,
 526                                ctx->reg_base + list->muxdiv_offset,
 527                                list->div_shift, list->div_flags, &ctx->lock);
 528                        break;
 529                case branch_factor:
 530                        clk = rockchip_clk_register_factor_branch(
 531                                list->name, list->parent_names,
 532                                list->num_parents, ctx->reg_base,
 533                                list->div_shift, list->div_width,
 534                                list->gate_offset, list->gate_shift,
 535                                list->gate_flags, flags, &ctx->lock);
 536                        break;
 537                case branch_ddrclk:
 538                        clk = rockchip_clk_register_ddrclk(
 539                                list->name, list->flags,
 540                                list->parent_names, list->num_parents,
 541                                list->muxdiv_offset, list->mux_shift,
 542                                list->mux_width, list->div_shift,
 543                                list->div_width, list->div_flags,
 544                                ctx->reg_base, &ctx->lock);
 545                        break;
 546                }
 547
 548                /* none of the cases above matched */
 549                if (!clk) {
 550                        pr_err("%s: unknown clock type %d\n",
 551                               __func__, list->branch_type);
 552                        continue;
 553                }
 554
 555                if (IS_ERR(clk)) {
 556                        pr_err("%s: failed to register clock %s: %ld\n",
 557                               __func__, list->name, PTR_ERR(clk));
 558                        continue;
 559                }
 560
 561                rockchip_clk_add_lookup(ctx, clk, list->id);
 562        }
 563}
 564EXPORT_SYMBOL_GPL(rockchip_clk_register_branches);
 565
 566void rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
 567                                  unsigned int lookup_id,
 568                                  const char *name, const char *const *parent_names,
 569                                  u8 num_parents,
 570                                  const struct rockchip_cpuclk_reg_data *reg_data,
 571                                  const struct rockchip_cpuclk_rate_table *rates,
 572                                  int nrates)
 573{
 574        struct clk *clk;
 575
 576        clk = rockchip_clk_register_cpuclk(name, parent_names, num_parents,
 577                                           reg_data, rates, nrates,
 578                                           ctx->reg_base, &ctx->lock);
 579        if (IS_ERR(clk)) {
 580                pr_err("%s: failed to register clock %s: %ld\n",
 581                       __func__, name, PTR_ERR(clk));
 582                return;
 583        }
 584
 585        rockchip_clk_add_lookup(ctx, clk, lookup_id);
 586}
 587EXPORT_SYMBOL_GPL(rockchip_clk_register_armclk);
 588
 589void rockchip_clk_protect_critical(const char *const clocks[],
 590                                   int nclocks)
 591{
 592        int i;
 593
 594        /* Protect the clocks that needs to stay on */
 595        for (i = 0; i < nclocks; i++) {
 596                struct clk *clk = __clk_lookup(clocks[i]);
 597
 598                clk_prepare_enable(clk);
 599        }
 600}
 601EXPORT_SYMBOL_GPL(rockchip_clk_protect_critical);
 602
 603static void __iomem *rst_base;
 604static unsigned int reg_restart;
 605static void (*cb_restart)(void);
 606static int rockchip_restart_notify(struct notifier_block *this,
 607                                   unsigned long mode, void *cmd)
 608{
 609        if (cb_restart)
 610                cb_restart();
 611
 612        writel(0xfdb9, rst_base + reg_restart);
 613        return NOTIFY_DONE;
 614}
 615
 616static struct notifier_block rockchip_restart_handler = {
 617        .notifier_call = rockchip_restart_notify,
 618        .priority = 128,
 619};
 620
 621void
 622rockchip_register_restart_notifier(struct rockchip_clk_provider *ctx,
 623                                   unsigned int reg,
 624                                   void (*cb)(void))
 625{
 626        int ret;
 627
 628        rst_base = ctx->reg_base;
 629        reg_restart = reg;
 630        cb_restart = cb;
 631        ret = register_restart_handler(&rockchip_restart_handler);
 632        if (ret)
 633                pr_err("%s: cannot register restart handler, %d\n",
 634                       __func__, ret);
 635}
 636EXPORT_SYMBOL_GPL(rockchip_register_restart_notifier);
 637