uboot/drivers/clk/at91/clk-utmi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * UTMI clock support for AT91 architectures.
   4 *
   5 * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
   6 *
   7 * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
   8 *
   9 * Based on drivers/clk/at91/clk-utmi.c from Linux.
  10 */
  11#include <asm/processor.h>
  12#include <common.h>
  13#include <clk-uclass.h>
  14#include <dm.h>
  15#include <linux/clk-provider.h>
  16#include <linux/clk/at91_pmc.h>
  17#include <mach/at91_sfr.h>
  18#include <regmap.h>
  19#include <syscon.h>
  20
  21#include "pmc.h"
  22
  23#define UBOOT_DM_CLK_AT91_UTMI                  "at91-utmi-clk"
  24#define UBOOT_DM_CLK_AT91_SAMA7G5_UTMI          "at91-sama7g5-utmi-clk"
  25
  26/*
  27 * The purpose of this clock is to generate a 480 MHz signal. A different
  28 * rate can't be configured.
  29 */
  30#define UTMI_RATE       480000000
  31
  32struct clk_utmi {
  33        void __iomem *base;
  34        struct regmap *regmap_sfr;
  35        struct clk clk;
  36};
  37
  38#define to_clk_utmi(_clk) container_of(_clk, struct clk_utmi, clk)
  39
  40static inline bool clk_utmi_ready(struct regmap *regmap)
  41{
  42        unsigned int status;
  43
  44        pmc_read(regmap, AT91_PMC_SR, &status);
  45
  46        return !!(status & AT91_PMC_LOCKU);
  47}
  48
  49static int clk_utmi_enable(struct clk *clk)
  50{
  51        struct clk_utmi *utmi = to_clk_utmi(clk);
  52        unsigned int uckr = AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT |
  53                            AT91_PMC_BIASEN;
  54        unsigned int utmi_ref_clk_freq;
  55        ulong parent_rate = clk_get_parent_rate(clk);
  56
  57        /*
  58         * If mainck rate is different from 12 MHz, we have to configure the
  59         * FREQ field of the SFR_UTMICKTRIM register to generate properly
  60         * the utmi clock.
  61         */
  62        switch (parent_rate) {
  63        case 12000000:
  64                utmi_ref_clk_freq = 0;
  65                break;
  66        case 16000000:
  67                utmi_ref_clk_freq = 1;
  68                break;
  69        case 24000000:
  70                utmi_ref_clk_freq = 2;
  71                break;
  72        /*
  73         * Not supported on SAMA5D2 but it's not an issue since MAINCK
  74         * maximum value is 24 MHz.
  75         */
  76        case 48000000:
  77                utmi_ref_clk_freq = 3;
  78                break;
  79        default:
  80                debug("UTMICK: unsupported mainck rate\n");
  81                return -EINVAL;
  82        }
  83
  84        if (utmi->regmap_sfr) {
  85                regmap_update_bits(utmi->regmap_sfr, AT91_SFR_UTMICKTRIM,
  86                                   AT91_UTMICKTRIM_FREQ, utmi_ref_clk_freq);
  87        } else if (utmi_ref_clk_freq) {
  88                debug("UTMICK: sfr node required\n");
  89                return -EINVAL;
  90        }
  91
  92        pmc_update_bits(utmi->base, AT91_CKGR_UCKR, uckr, uckr);
  93
  94        while (!clk_utmi_ready(utmi->base)) {
  95                debug("waiting for utmi...\n");
  96                cpu_relax();
  97        }
  98
  99        return 0;
 100}
 101
 102static int clk_utmi_disable(struct clk *clk)
 103{
 104        struct clk_utmi *utmi = to_clk_utmi(clk);
 105
 106        pmc_update_bits(utmi->base, AT91_CKGR_UCKR, AT91_PMC_UPLLEN, 0);
 107
 108        return 0;
 109}
 110
 111static ulong clk_utmi_get_rate(struct clk *clk)
 112{
 113        /* UTMI clk rate is fixed. */
 114        return UTMI_RATE;
 115}
 116
 117static const struct clk_ops utmi_ops = {
 118        .enable = clk_utmi_enable,
 119        .disable = clk_utmi_disable,
 120        .get_rate = clk_utmi_get_rate,
 121};
 122
 123struct clk *at91_clk_register_utmi(void __iomem *base, struct udevice *dev,
 124                                   const char *name, const char *parent_name)
 125{
 126        struct udevice *syscon;
 127        struct clk_utmi *utmi;
 128        struct clk *clk;
 129        int ret;
 130
 131        if (!base || !dev || !name || !parent_name)
 132                return ERR_PTR(-EINVAL);
 133
 134        ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev,
 135                                           "regmap-sfr", &syscon);
 136        if (ret)
 137                return ERR_PTR(ret);
 138
 139        utmi = kzalloc(sizeof(*utmi), GFP_KERNEL);
 140        if (!utmi)
 141                return ERR_PTR(-ENOMEM);
 142
 143        utmi->base = base;
 144        utmi->regmap_sfr = syscon_get_regmap(syscon);
 145        if (!utmi->regmap_sfr) {
 146                kfree(utmi);
 147                return ERR_PTR(-ENODEV);
 148        }
 149
 150        clk = &utmi->clk;
 151        clk->flags = CLK_GET_RATE_NOCACHE;
 152        ret = clk_register(clk, UBOOT_DM_CLK_AT91_UTMI, name, parent_name);
 153        if (ret) {
 154                kfree(utmi);
 155                clk = ERR_PTR(ret);
 156        }
 157
 158        return clk;
 159}
 160
 161U_BOOT_DRIVER(at91_utmi_clk) = {
 162        .name = UBOOT_DM_CLK_AT91_UTMI,
 163        .id = UCLASS_CLK,
 164        .ops = &utmi_ops,
 165        .flags = DM_FLAG_PRE_RELOC,
 166};
 167
 168static int clk_utmi_sama7g5_enable(struct clk *clk)
 169{
 170        struct clk_utmi *utmi = to_clk_utmi(clk);
 171        ulong parent_rate = clk_get_parent_rate(clk);
 172        unsigned int val;
 173
 174        switch (parent_rate) {
 175        case 16000000:
 176                val = 0;
 177                break;
 178        case 20000000:
 179                val = 2;
 180                break;
 181        case 24000000:
 182                val = 3;
 183                break;
 184        case 32000000:
 185                val = 5;
 186                break;
 187        default:
 188                debug("UTMICK: unsupported main_xtal rate\n");
 189                return -EINVAL;
 190        }
 191
 192        pmc_write(utmi->base, AT91_PMC_XTALF, val);
 193
 194        return 0;
 195}
 196
 197static const struct clk_ops sama7g5_utmi_ops = {
 198        .enable = clk_utmi_sama7g5_enable,
 199        .get_rate = clk_utmi_get_rate,
 200};
 201
 202struct clk *at91_clk_sama7g5_register_utmi(void __iomem *base,
 203                const char *name, const char *parent_name)
 204{
 205        struct clk_utmi *utmi;
 206        struct clk *clk;
 207        int ret;
 208
 209        if (!base || !name || !parent_name)
 210                return ERR_PTR(-EINVAL);
 211
 212        utmi = kzalloc(sizeof(*utmi), GFP_KERNEL);
 213        if (!utmi)
 214                return ERR_PTR(-ENOMEM);
 215
 216        utmi->base = base;
 217
 218        clk = &utmi->clk;
 219        ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAMA7G5_UTMI, name,
 220                           parent_name);
 221        if (ret) {
 222                kfree(utmi);
 223                clk = ERR_PTR(ret);
 224        }
 225
 226        return clk;
 227}
 228
 229U_BOOT_DRIVER(at91_sama7g5_utmi_clk) = {
 230        .name = UBOOT_DM_CLK_AT91_SAMA7G5_UTMI,
 231        .id = UCLASS_CLK,
 232        .ops = &sama7g5_utmi_ops,
 233        .flags = DM_FLAG_PRE_RELOC,
 234};
 235