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