uboot/drivers/clk/at91/sckc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Slow 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
  10#include <common.h>
  11#include <clk-uclass.h>
  12#include <dm.h>
  13#include <dt-bindings/clk/at91.h>
  14#include <linux/clk-provider.h>
  15
  16#include "pmc.h"
  17
  18#define UBOOT_DM_CLK_AT91_SAM9X60_TD_SLCK       "at91-sam9x60-td-slck"
  19#define UBOOT_DM_CLK_AT91_SCKC                  "at91-sckc"
  20
  21#define AT91_OSC_SEL            BIT(24)
  22#define AT91_OSC_SEL_SHIFT      (24)
  23
  24struct sam9x60_sckc {
  25        void __iomem *reg;
  26        const char **parent_names;
  27        unsigned int num_parents;
  28        struct clk clk;
  29};
  30
  31#define to_sam9x60_sckc(c)      container_of(c, struct sam9x60_sckc, clk)
  32
  33static int sam9x60_sckc_of_xlate(struct clk *clk,
  34                                 struct ofnode_phandle_args *args)
  35{
  36        if (args->args_count != 1) {
  37                debug("AT91: SCKC: Invalid args_count: %d\n", args->args_count);
  38                return -EINVAL;
  39        }
  40
  41        clk->id = AT91_TO_CLK_ID(PMC_TYPE_SLOW, args->args[0]);
  42
  43        return 0;
  44}
  45
  46static const struct clk_ops sam9x60_sckc_ops = {
  47        .of_xlate = sam9x60_sckc_of_xlate,
  48        .get_rate = clk_generic_get_rate,
  49};
  50
  51static int sam9x60_td_slck_set_parent(struct clk *clk, struct clk *parent)
  52{
  53        struct sam9x60_sckc *sckc = to_sam9x60_sckc(clk);
  54        u32 i;
  55
  56        for (i = 0; i < sckc->num_parents; i++) {
  57                if (!strcmp(parent->dev->name, sckc->parent_names[i]))
  58                        break;
  59        }
  60        if (i == sckc->num_parents)
  61                return -EINVAL;
  62
  63        pmc_update_bits(sckc->reg, 0, AT91_OSC_SEL, (i << AT91_OSC_SEL_SHIFT));
  64
  65        return 0;
  66}
  67
  68static const struct clk_ops sam9x60_td_slck_ops = {
  69        .get_rate = clk_generic_get_rate,
  70        .set_parent = sam9x60_td_slck_set_parent,
  71};
  72
  73static struct clk *at91_sam9x60_clk_register_td_slck(struct sam9x60_sckc *sckc,
  74                const char *name, const char * const *parent_names,
  75                int num_parents)
  76{
  77        struct clk *clk;
  78        int ret = -ENOMEM;
  79        u32 val, i;
  80
  81        if (!sckc || !name || !parent_names || num_parents != 2)
  82                return ERR_PTR(-EINVAL);
  83
  84        sckc->parent_names = kzalloc(sizeof(*sckc->parent_names) * num_parents,
  85                                     GFP_KERNEL);
  86        if (!sckc->parent_names)
  87                return ERR_PTR(ret);
  88
  89        for (i = 0; i < num_parents; i++) {
  90                sckc->parent_names[i] = kmemdup(parent_names[i],
  91                                strlen(parent_names[i]) + 1, GFP_KERNEL);
  92                if (!sckc->parent_names[i])
  93                        goto free;
  94        }
  95        sckc->num_parents = num_parents;
  96
  97        pmc_read(sckc->reg, 0, &val);
  98        val = (val & AT91_OSC_SEL) >> AT91_OSC_SEL_SHIFT;
  99
 100        clk = &sckc->clk;
 101        ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAM9X60_TD_SLCK, name,
 102                           parent_names[val]);
 103        if (ret)
 104                goto free;
 105
 106        return clk;
 107
 108free:
 109        for (; i >= 0; i--)
 110                kfree(sckc->parent_names[i]);
 111        kfree(sckc->parent_names);
 112
 113        return ERR_PTR(ret);
 114}
 115
 116U_BOOT_DRIVER(at91_sam9x60_td_slck) = {
 117        .name = UBOOT_DM_CLK_AT91_SAM9X60_TD_SLCK,
 118        .id = UCLASS_CLK,
 119        .ops = &sam9x60_td_slck_ops,
 120        .flags = DM_FLAG_PRE_RELOC,
 121};
 122
 123static int at91_sam9x60_sckc_probe(struct udevice *dev)
 124{
 125        struct sam9x60_sckc *sckc = dev_get_priv(dev);
 126        void __iomem *base = (void *)devfdt_get_addr(dev);
 127        const char *slow_rc_osc, *slow_osc;
 128        const char *parents[2];
 129        struct clk *clk, c;
 130        int ret;
 131
 132        ret = clk_get_by_index(dev, 0, &c);
 133        if (ret)
 134                return ret;
 135        slow_rc_osc = clk_hw_get_name(&c);
 136
 137        ret = clk_get_by_index(dev, 1, &c);
 138        if (ret)
 139                return ret;
 140        slow_osc = clk_hw_get_name(&c);
 141
 142        clk = clk_register_fixed_factor(NULL, "md_slck", slow_rc_osc, 0, 1, 1);
 143        if (IS_ERR(clk))
 144                return PTR_ERR(clk);
 145        clk_dm(AT91_TO_CLK_ID(PMC_TYPE_SLOW, 0), clk);
 146
 147        parents[0] = slow_rc_osc;
 148        parents[1] = slow_osc;
 149        sckc[1].reg = base;
 150        clk = at91_sam9x60_clk_register_td_slck(&sckc[1], "td_slck",
 151                                                parents, 2);
 152        if (IS_ERR(clk))
 153                return PTR_ERR(clk);
 154        clk_dm(AT91_TO_CLK_ID(PMC_TYPE_SLOW, 1), clk);
 155
 156        return 0;
 157}
 158
 159static const struct udevice_id sam9x60_sckc_ids[] = {
 160        { .compatible = "microchip,sam9x60-sckc" },
 161        { /* Sentinel. */ },
 162};
 163
 164U_BOOT_DRIVER(at91_sckc) = {
 165        .name = UBOOT_DM_CLK_AT91_SCKC,
 166        .id = UCLASS_CLK,
 167        .of_match = sam9x60_sckc_ids,
 168        .priv_auto      = sizeof(struct sam9x60_sckc) * 2,
 169        .ops = &sam9x60_sckc_ops,
 170        .probe = at91_sam9x60_sckc_probe,
 171        .flags = DM_FLAG_PRE_RELOC,
 172};
 173