uboot/arch/arm/mach-snapdragon/clock-snapdragon.c
<<
>>
Prefs
   1// SPDX-License-Identifier: BSD-3-Clause
   2/*
   3 * Clock drivers for Qualcomm APQ8016, APQ8096
   4 *
   5 * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
   6 *
   7 * Based on Little Kernel driver, simplified
   8 */
   9
  10#include <common.h>
  11#include <clk-uclass.h>
  12#include <dm.h>
  13#include <errno.h>
  14#include <asm/io.h>
  15#include <linux/bitops.h>
  16#include "clock-snapdragon.h"
  17
  18/* CBCR register fields */
  19#define CBCR_BRANCH_ENABLE_BIT  BIT(0)
  20#define CBCR_BRANCH_OFF_BIT     BIT(31)
  21
  22extern ulong msm_set_rate(struct clk *clk, ulong rate);
  23
  24/* Enable clock controlled by CBC soft macro */
  25void clk_enable_cbc(phys_addr_t cbcr)
  26{
  27        setbits_le32(cbcr, CBCR_BRANCH_ENABLE_BIT);
  28
  29        while (readl(cbcr) & CBCR_BRANCH_OFF_BIT)
  30                ;
  31}
  32
  33void clk_enable_gpll0(phys_addr_t base, const struct gpll0_ctrl *gpll0)
  34{
  35        if (readl(base + gpll0->status) & gpll0->status_bit)
  36                return; /* clock already enabled */
  37
  38        setbits_le32(base + gpll0->ena_vote, gpll0->vote_bit);
  39
  40        while ((readl(base + gpll0->status) & gpll0->status_bit) == 0)
  41                ;
  42}
  43
  44#define APPS_CMD_RGCR_UPDATE BIT(0)
  45
  46/* Update clock command via CMD_RGCR */
  47void clk_bcr_update(phys_addr_t apps_cmd_rgcr)
  48{
  49        setbits_le32(apps_cmd_rgcr, APPS_CMD_RGCR_UPDATE);
  50
  51        /* Wait for frequency to be updated. */
  52        while (readl(apps_cmd_rgcr) & APPS_CMD_RGCR_UPDATE)
  53                ;
  54}
  55
  56#define CFG_MODE_DUAL_EDGE (0x2 << 12) /* Counter mode */
  57
  58#define CFG_MASK 0x3FFF
  59
  60#define CFG_DIVIDER_MASK 0x1F
  61
  62/* root set rate for clocks with half integer and MND divider */
  63void clk_rcg_set_rate_mnd(phys_addr_t base, const struct bcr_regs *regs,
  64                          int div, int m, int n, int source)
  65{
  66        u32 cfg;
  67        /* M value for MND divider. */
  68        u32 m_val = m;
  69        /* NOT(N-M) value for MND divider. */
  70        u32 n_val = ~((n) - (m)) * !!(n);
  71        /* NOT 2D value for MND divider. */
  72        u32 d_val = ~(n);
  73
  74        /* Program MND values */
  75        writel(m_val, base + regs->M);
  76        writel(n_val, base + regs->N);
  77        writel(d_val, base + regs->D);
  78
  79        /* setup src select and divider */
  80        cfg  = readl(base + regs->cfg_rcgr);
  81        cfg &= ~CFG_MASK;
  82        cfg |= source & CFG_CLK_SRC_MASK; /* Select clock source */
  83
  84        /* Set the divider; HW permits fraction dividers (+0.5), but
  85           for simplicity, we will support integers only */
  86        if (div)
  87                cfg |= (2 * div - 1) & CFG_DIVIDER_MASK;
  88
  89        if (n_val)
  90                cfg |= CFG_MODE_DUAL_EDGE;
  91
  92        writel(cfg, base + regs->cfg_rcgr); /* Write new clock configuration */
  93
  94        /* Inform h/w to start using the new config. */
  95        clk_bcr_update(base + regs->cmd_rcgr);
  96}
  97
  98static int msm_clk_probe(struct udevice *dev)
  99{
 100        struct msm_clk_priv *priv = dev_get_priv(dev);
 101
 102        priv->base = devfdt_get_addr(dev);
 103        if (priv->base == FDT_ADDR_T_NONE)
 104                return -EINVAL;
 105
 106        return 0;
 107}
 108
 109static ulong msm_clk_set_rate(struct clk *clk, ulong rate)
 110{
 111        return msm_set_rate(clk, rate);
 112}
 113
 114static struct clk_ops msm_clk_ops = {
 115        .set_rate = msm_clk_set_rate,
 116};
 117
 118static const struct udevice_id msm_clk_ids[] = {
 119        { .compatible = "qcom,gcc-msm8916" },
 120        { .compatible = "qcom,gcc-apq8016" },
 121        { .compatible = "qcom,gcc-msm8996" },
 122        { .compatible = "qcom,gcc-apq8096" },
 123        { }
 124};
 125
 126U_BOOT_DRIVER(clk_msm) = {
 127        .name           = "clk_msm",
 128        .id             = UCLASS_CLK,
 129        .of_match       = msm_clk_ids,
 130        .ops            = &msm_clk_ops,
 131        .priv_auto_alloc_size = sizeof(struct msm_clk_priv),
 132        .probe          = msm_clk_probe,
 133};
 134