uboot/arch/arm/mach-imx/mx7ulp/pcc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2016 Freescale Semiconductor, Inc.
   4 */
   5
   6#include <common.h>
   7#include <div64.h>
   8#include <asm/io.h>
   9#include <errno.h>
  10#include <asm/arch/imx-regs.h>
  11#include <asm/arch/pcc.h>
  12#include <asm/arch/sys_proto.h>
  13
  14#define PCC_CLKSRC_TYPES 2
  15#define PCC_CLKSRC_NUM 7
  16
  17static enum scg_clk pcc_clksrc[PCC_CLKSRC_TYPES][PCC_CLKSRC_NUM] = {
  18        {       SCG_NIC1_BUS_CLK,
  19                SCG_NIC1_CLK,
  20                SCG_DDR_CLK,
  21                SCG_APLL_PFD2_CLK,
  22                SCG_APLL_PFD1_CLK,
  23                SCG_APLL_PFD0_CLK,
  24                USB_PLL_OUT,
  25        },
  26        {       SCG_SOSC_DIV2_CLK,  /* SOSC BUS clock */
  27                MIPI_PLL_OUT,
  28                SCG_FIRC_DIV2_CLK,  /* FIRC BUS clock */
  29                SCG_ROSC_CLK,
  30                SCG_NIC1_BUS_CLK,
  31                SCG_NIC1_CLK,
  32                SCG_APLL_PFD3_CLK,
  33        },
  34};
  35
  36static struct pcc_entry pcc_arrays[] = {
  37        {PCC2_RBASE, DMA1_PCC2_SLOT,            CLKSRC_NO_PCS, PCC_NO_DIV},
  38        {PCC2_RBASE, RGPIO1_PCC2_SLOT,          CLKSRC_NO_PCS, PCC_NO_DIV},
  39        {PCC2_RBASE, FLEXBUS0_PCC2_SLOT,        CLKSRC_NO_PCS, PCC_NO_DIV},
  40        {PCC2_RBASE, SEMA42_1_PCC2_SLOT,        CLKSRC_NO_PCS, PCC_NO_DIV},
  41        {PCC2_RBASE, DMA1_CH_MUX0_PCC2_SLOT,    CLKSRC_NO_PCS, PCC_NO_DIV},
  42        {PCC2_RBASE, SNVS_PCC2_SLOT,            CLKSRC_NO_PCS, PCC_NO_DIV},
  43        {PCC2_RBASE, CAAM_PCC2_SLOT,            CLKSRC_NO_PCS, PCC_NO_DIV},
  44        {PCC2_RBASE, LPTPM4_PCC2_SLOT,          CLKSRC_PER_BUS, PCC_NO_DIV},
  45        {PCC2_RBASE, LPTPM5_PCC2_SLOT,          CLKSRC_PER_BUS, PCC_NO_DIV},
  46        {PCC2_RBASE, LPIT1_PCC2_SLOT,           CLKSRC_PER_BUS, PCC_NO_DIV},
  47        {PCC2_RBASE, LPSPI2_PCC2_SLOT,          CLKSRC_PER_BUS, PCC_NO_DIV},
  48        {PCC2_RBASE, LPSPI3_PCC2_SLOT,          CLKSRC_PER_BUS, PCC_NO_DIV},
  49        {PCC2_RBASE, LPI2C4_PCC2_SLOT,          CLKSRC_PER_BUS, PCC_NO_DIV},
  50        {PCC2_RBASE, LPI2C5_PCC2_SLOT,          CLKSRC_PER_BUS, PCC_NO_DIV},
  51        {PCC2_RBASE, LPUART4_PCC2_SLOT,         CLKSRC_PER_BUS, PCC_NO_DIV},
  52        {PCC2_RBASE, LPUART5_PCC2_SLOT,         CLKSRC_PER_BUS, PCC_NO_DIV},
  53        {PCC2_RBASE, FLEXIO1_PCC2_SLOT,         CLKSRC_PER_BUS, PCC_NO_DIV},
  54        {PCC2_RBASE, USBOTG0_PCC2_SLOT,         CLKSRC_PER_PLAT, PCC_HAS_DIV},
  55        {PCC2_RBASE, USBOTG1_PCC2_SLOT,         CLKSRC_PER_PLAT, PCC_HAS_DIV},
  56        {PCC2_RBASE, USBPHY_PCC2_SLOT,          CLKSRC_NO_PCS, PCC_NO_DIV},
  57        {PCC2_RBASE, USB_PL301_PCC2_SLOT,       CLKSRC_NO_PCS, PCC_NO_DIV},
  58        {PCC2_RBASE, USDHC0_PCC2_SLOT,          CLKSRC_PER_PLAT, PCC_HAS_DIV},
  59        {PCC2_RBASE, USDHC1_PCC2_SLOT,          CLKSRC_PER_PLAT, PCC_HAS_DIV},
  60        {PCC2_RBASE, WDG1_PCC2_SLOT,            CLKSRC_PER_BUS, PCC_HAS_DIV},
  61        {PCC2_RBASE, WDG2_PCC2_SLOT,            CLKSRC_PER_BUS, PCC_HAS_DIV},
  62
  63        {PCC3_RBASE, LPTPM6_PCC3_SLOT,          CLKSRC_PER_BUS, PCC_NO_DIV},
  64        {PCC3_RBASE, LPTPM7_PCC3_SLOT,          CLKSRC_PER_BUS, PCC_NO_DIV},
  65        {PCC3_RBASE, LPI2C6_PCC3_SLOT,          CLKSRC_PER_BUS, PCC_NO_DIV},
  66        {PCC3_RBASE, LPI2C7_PCC3_SLOT,          CLKSRC_PER_BUS, PCC_NO_DIV},
  67        {PCC3_RBASE, LPUART6_PCC3_SLOT,         CLKSRC_PER_BUS, PCC_NO_DIV},
  68        {PCC3_RBASE, LPUART7_PCC3_SLOT,         CLKSRC_PER_BUS, PCC_NO_DIV},
  69        {PCC3_RBASE, VIU0_PCC3_SLOT,            CLKSRC_NO_PCS, PCC_NO_DIV},
  70        {PCC3_RBASE, DSI0_PCC3_SLOT,            CLKSRC_PER_BUS, PCC_HAS_DIV},
  71        {PCC3_RBASE, LCDIF0_PCC3_SLOT,          CLKSRC_PER_PLAT, PCC_HAS_DIV},
  72        {PCC3_RBASE, MMDC0_PCC3_SLOT,           CLKSRC_NO_PCS, PCC_NO_DIV},
  73        {PCC3_RBASE, PORTC_PCC3_SLOT,           CLKSRC_NO_PCS, PCC_NO_DIV},
  74        {PCC3_RBASE, PORTD_PCC3_SLOT,           CLKSRC_NO_PCS, PCC_NO_DIV},
  75        {PCC3_RBASE, PORTE_PCC3_SLOT,           CLKSRC_NO_PCS, PCC_NO_DIV},
  76        {PCC3_RBASE, PORTF_PCC3_SLOT,           CLKSRC_NO_PCS, PCC_NO_DIV},
  77        {PCC3_RBASE, GPU3D_PCC3_SLOT,           CLKSRC_PER_PLAT, PCC_NO_DIV},
  78        {PCC3_RBASE, GPU2D_PCC3_SLOT,           CLKSRC_PER_PLAT, PCC_NO_DIV},
  79};
  80
  81int pcc_clock_enable(enum pcc_clk clk, bool enable)
  82{
  83        u32 reg, val;
  84
  85        if (clk >= ARRAY_SIZE(pcc_arrays))
  86                return -EINVAL;
  87
  88        reg = pcc_arrays[clk].pcc_base + pcc_arrays[clk].pcc_slot * 4;
  89
  90        val = readl(reg);
  91
  92        clk_debug("pcc_clock_enable: clk %d, reg 0x%x, val 0x%x, enable %d\n",
  93                  clk, reg, val, enable);
  94
  95        if (!(val & PCC_PR_MASK) || (val & PCC_INUSE_MASK))
  96                return -EPERM;
  97
  98        if (enable)
  99                val |= PCC_CGC_MASK;
 100        else
 101                val &= ~PCC_CGC_MASK;
 102
 103        writel(val, reg);
 104
 105        clk_debug("pcc_clock_enable: val 0x%x\n", val);
 106
 107        return 0;
 108}
 109
 110/* The clock source select needs clock is disabled */
 111int pcc_clock_sel(enum pcc_clk clk, enum scg_clk src)
 112{
 113        u32 reg, val, i, clksrc_type;
 114
 115        if (clk >= ARRAY_SIZE(pcc_arrays))
 116                return -EINVAL;
 117
 118        clksrc_type = pcc_arrays[clk].clksrc;
 119        if (clksrc_type >= CLKSRC_NO_PCS) {
 120                printf("No PCS field for the PCC %d, clksrc type %d\n",
 121                       clk, clksrc_type);
 122                return -EPERM;
 123        }
 124
 125        for (i = 0; i < PCC_CLKSRC_NUM; i++) {
 126                if (pcc_clksrc[clksrc_type][i] == src) {
 127                        /* Find the clock src, then set it to PCS */
 128                        break;
 129                }
 130        }
 131
 132        if (i == PCC_CLKSRC_NUM) {
 133                printf("Not find the parent scg_clk in PCS of PCC %d, invalid scg_clk %d\n", clk, src);
 134                return -EINVAL;
 135        }
 136
 137        reg = pcc_arrays[clk].pcc_base + pcc_arrays[clk].pcc_slot * 4;
 138
 139        val = readl(reg);
 140
 141        clk_debug("pcc_clock_sel: clk %d, reg 0x%x, val 0x%x, clksrc_type %d\n",
 142                  clk, reg, val, clksrc_type);
 143
 144        if (!(val & PCC_PR_MASK) || (val & PCC_INUSE_MASK) ||
 145            (val & PCC_CGC_MASK)) {
 146                printf("Not permit to select clock source val = 0x%x\n", val);
 147                return -EPERM;
 148        }
 149
 150        val &= ~PCC_PCS_MASK;
 151        val |= ((i + 1) << PCC_PCS_OFFSET);
 152
 153        writel(val, reg);
 154
 155        clk_debug("pcc_clock_sel: val 0x%x\n", val);
 156
 157        return 0;
 158}
 159
 160int pcc_clock_div_config(enum pcc_clk clk, bool frac, u8 div)
 161{
 162        u32 reg, val;
 163
 164        if (clk >= ARRAY_SIZE(pcc_arrays) || div > 8 ||
 165            (div == 1 && frac != 0))
 166                return -EINVAL;
 167
 168        if (pcc_arrays[clk].div >= PCC_NO_DIV) {
 169                printf("No DIV/FRAC field for the PCC %d\n", clk);
 170                return -EPERM;
 171        }
 172
 173        reg = pcc_arrays[clk].pcc_base + pcc_arrays[clk].pcc_slot * 4;
 174
 175        val = readl(reg);
 176
 177        if (!(val & PCC_PR_MASK) || (val & PCC_INUSE_MASK) ||
 178            (val & PCC_CGC_MASK)) {
 179                printf("Not permit to set div/frac val = 0x%x\n", val);
 180                return -EPERM;
 181        }
 182
 183        if (frac)
 184                val |= PCC_FRAC_MASK;
 185        else
 186                val &= ~PCC_FRAC_MASK;
 187
 188        val &= ~PCC_PCD_MASK;
 189        val |= (div - 1) & PCC_PCD_MASK;
 190
 191        writel(val, reg);
 192
 193        return 0;
 194}
 195
 196bool pcc_clock_is_enable(enum pcc_clk clk)
 197{
 198        u32 reg, val;
 199
 200        if (clk >= ARRAY_SIZE(pcc_arrays))
 201                return -EINVAL;
 202
 203        reg = pcc_arrays[clk].pcc_base + pcc_arrays[clk].pcc_slot * 4;
 204        val = readl(reg);
 205
 206        if ((val & PCC_INUSE_MASK) || (val & PCC_CGC_MASK))
 207                return true;
 208
 209        return false;
 210}
 211
 212int pcc_clock_get_clksrc(enum pcc_clk clk, enum scg_clk *src)
 213{
 214        u32 reg, val, clksrc_type;
 215
 216        if (clk >= ARRAY_SIZE(pcc_arrays))
 217                return -EINVAL;
 218
 219        clksrc_type = pcc_arrays[clk].clksrc;
 220        if (clksrc_type >= CLKSRC_NO_PCS) {
 221                printf("No PCS field for the PCC %d, clksrc type %d\n",
 222                       clk, clksrc_type);
 223                return -EPERM;
 224        }
 225
 226        reg = pcc_arrays[clk].pcc_base + pcc_arrays[clk].pcc_slot * 4;
 227
 228        val = readl(reg);
 229
 230        clk_debug("pcc_clock_get_clksrc: clk %d, reg 0x%x, val 0x%x, type %d\n",
 231                  clk, reg, val, clksrc_type);
 232
 233        if (!(val & PCC_PR_MASK)) {
 234                printf("This pcc slot is not present = 0x%x\n", val);
 235                return -EPERM;
 236        }
 237
 238        val &= PCC_PCS_MASK;
 239        val = (val >> PCC_PCS_OFFSET);
 240
 241        if (!val) {
 242                printf("Clock source is off\n");
 243                return -EIO;
 244        }
 245
 246        *src = pcc_clksrc[clksrc_type][val - 1];
 247
 248        clk_debug("pcc_clock_get_clksrc: parent scg clk %d\n", *src);
 249
 250        return 0;
 251}
 252
 253u32 pcc_clock_get_rate(enum pcc_clk clk)
 254{
 255        u32 reg, val, rate, frac, div;
 256        enum scg_clk parent;
 257        int ret;
 258
 259        ret = pcc_clock_get_clksrc(clk, &parent);
 260        if (ret)
 261                return 0;
 262
 263        rate = scg_clk_get_rate(parent);
 264
 265        clk_debug("pcc_clock_get_rate: parent rate %u\n", rate);
 266
 267        if (pcc_arrays[clk].div == PCC_HAS_DIV) {
 268                reg = pcc_arrays[clk].pcc_base + pcc_arrays[clk].pcc_slot * 4;
 269                val = readl(reg);
 270
 271                frac = (val & PCC_FRAC_MASK) >> PCC_FRAC_OFFSET;
 272                div = (val & PCC_PCD_MASK) >> PCC_PCD_OFFSET;
 273
 274                /*
 275                 * Theoretically don't have overflow in the calc,
 276                 * the rate won't exceed 2G
 277                 */
 278                rate = rate * (frac + 1) / (div + 1);
 279        }
 280
 281        clk_debug("pcc_clock_get_rate: rate %u\n", rate);
 282        return rate;
 283}
 284