uboot/drivers/clk/at91/clk-generated.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2016 Atmel Corporation
   4 *               Wenyou.Yang <wenyou.yang@atmel.com>
   5 */
   6
   7#include <common.h>
   8#include <clk-uclass.h>
   9#include <dm.h>
  10#include <linux/io.h>
  11#include <mach/at91_pmc.h>
  12#include "pmc.h"
  13
  14DECLARE_GLOBAL_DATA_PTR;
  15
  16#define GENERATED_SOURCE_MAX    6
  17#define GENERATED_MAX_DIV       255
  18
  19/**
  20 * generated_clk_bind() - for the generated clock driver
  21 * Recursively bind its children as clk devices.
  22 *
  23 * @return: 0 on success, or negative error code on failure
  24 */
  25static int generated_clk_bind(struct udevice *dev)
  26{
  27        return at91_clk_sub_device_bind(dev, "generic-clk");
  28}
  29
  30static const struct udevice_id generated_clk_match[] = {
  31        { .compatible = "atmel,sama5d2-clk-generated" },
  32        {}
  33};
  34
  35U_BOOT_DRIVER(generated_clk) = {
  36        .name = "generated-clk",
  37        .id = UCLASS_MISC,
  38        .of_match = generated_clk_match,
  39        .bind = generated_clk_bind,
  40};
  41
  42/*-------------------------------------------------------------*/
  43
  44struct generic_clk_priv {
  45        u32 num_parents;
  46};
  47
  48static ulong generic_clk_get_rate(struct clk *clk)
  49{
  50        struct pmc_platdata *plat = dev_get_platdata(clk->dev);
  51        struct at91_pmc *pmc = plat->reg_base;
  52        struct clk parent;
  53        ulong clk_rate;
  54        u32 tmp, gckdiv;
  55        u8 clock_source, parent_index;
  56        int ret;
  57
  58        writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr);
  59        tmp = readl(&pmc->pcr);
  60        clock_source = (tmp >> AT91_PMC_PCR_GCKCSS_OFFSET) &
  61                    AT91_PMC_PCR_GCKCSS_MASK;
  62        gckdiv = (tmp >> AT91_PMC_PCR_GCKDIV_OFFSET) & AT91_PMC_PCR_GCKDIV_MASK;
  63
  64        parent_index = clock_source - 1;
  65        ret = clk_get_by_index(dev_get_parent(clk->dev), parent_index, &parent);
  66        if (ret)
  67                return 0;
  68
  69        clk_rate = clk_get_rate(&parent) / (gckdiv + 1);
  70
  71        clk_free(&parent);
  72
  73        return clk_rate;
  74}
  75
  76static ulong generic_clk_set_rate(struct clk *clk, ulong rate)
  77{
  78        struct pmc_platdata *plat = dev_get_platdata(clk->dev);
  79        struct at91_pmc *pmc = plat->reg_base;
  80        struct generic_clk_priv *priv = dev_get_priv(clk->dev);
  81        struct clk parent, best_parent;
  82        ulong tmp_rate, best_rate = rate, parent_rate;
  83        int tmp_diff, best_diff = -1;
  84        u32 div, best_div = 0;
  85        u8 best_parent_index, best_clock_source = 0;
  86        u8 i;
  87        u32 tmp;
  88        int ret;
  89
  90        for (i = 0; i < priv->num_parents; i++) {
  91                ret = clk_get_by_index(dev_get_parent(clk->dev), i, &parent);
  92                if (ret)
  93                        return ret;
  94
  95                parent_rate = clk_get_rate(&parent);
  96                if (IS_ERR_VALUE(parent_rate))
  97                        return parent_rate;
  98
  99                for (div = 1; div < GENERATED_MAX_DIV + 2; div++) {
 100                        tmp_rate = DIV_ROUND_CLOSEST(parent_rate, div);
 101                        tmp_diff = abs(rate - tmp_rate);
 102
 103                        if (best_diff < 0 || best_diff > tmp_diff) {
 104                                best_rate = tmp_rate;
 105                                best_diff = tmp_diff;
 106
 107                                best_div = div - 1;
 108                                best_parent = parent;
 109                                best_parent_index = i;
 110                                best_clock_source = best_parent_index + 1;
 111                        }
 112
 113                        if (!best_diff || tmp_rate < rate)
 114                                break;
 115                }
 116
 117                if (!best_diff)
 118                        break;
 119        }
 120
 121        debug("GCK: best parent: %s, best_rate = %ld, best_div = %d\n",
 122              best_parent.dev->name, best_rate, best_div);
 123
 124        ret = clk_enable(&best_parent);
 125        if (ret)
 126                return ret;
 127
 128        writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr);
 129        tmp = readl(&pmc->pcr);
 130        tmp &= ~(AT91_PMC_PCR_GCKDIV | AT91_PMC_PCR_GCKCSS);
 131        tmp |= AT91_PMC_PCR_GCKCSS_(best_clock_source) |
 132               AT91_PMC_PCR_CMD_WRITE |
 133               AT91_PMC_PCR_GCKDIV_(best_div) |
 134               AT91_PMC_PCR_GCKEN;
 135        writel(tmp, &pmc->pcr);
 136
 137        while (!(readl(&pmc->sr) & AT91_PMC_GCKRDY))
 138                ;
 139
 140        return 0;
 141}
 142
 143static struct clk_ops generic_clk_ops = {
 144        .of_xlate = at91_clk_of_xlate,
 145        .get_rate = generic_clk_get_rate,
 146        .set_rate = generic_clk_set_rate,
 147};
 148
 149static int generic_clk_ofdata_to_platdata(struct udevice *dev)
 150{
 151        struct generic_clk_priv *priv = dev_get_priv(dev);
 152        u32 cells[GENERATED_SOURCE_MAX];
 153        u32 num_parents;
 154
 155        num_parents = fdtdec_get_int_array_count(gd->fdt_blob,
 156                        dev_of_offset(dev_get_parent(dev)), "clocks", cells,
 157                        GENERATED_SOURCE_MAX);
 158
 159        if (!num_parents)
 160                return -1;
 161
 162        priv->num_parents = num_parents;
 163
 164        return 0;
 165}
 166
 167U_BOOT_DRIVER(generic_clk) = {
 168        .name = "generic-clk",
 169        .id = UCLASS_CLK,
 170        .probe = at91_clk_probe,
 171        .ofdata_to_platdata = generic_clk_ofdata_to_platdata,
 172        .priv_auto_alloc_size = sizeof(struct generic_clk_priv),
 173        .platdata_auto_alloc_size = sizeof(struct pmc_platdata),
 174        .ops = &generic_clk_ops,
 175};
 176