linux/drivers/clk/versatile/clk-vexpress-osc.c
<<
>>
Prefs
   1/*
   2 * This program is free software; you can redistribute it and/or modify
   3 * it under the terms of the GNU General Public License version 2 as
   4 * published by the Free Software Foundation.
   5 *
   6 * This program is distributed in the hope that it will be useful,
   7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
   8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   9 * GNU General Public License for more details.
  10 *
  11 * Copyright (C) 2012 ARM Limited
  12 */
  13
  14#include <linux/clkdev.h>
  15#include <linux/clk-provider.h>
  16#include <linux/err.h>
  17#include <linux/of.h>
  18#include <linux/platform_device.h>
  19#include <linux/slab.h>
  20#include <linux/vexpress.h>
  21
  22struct vexpress_osc {
  23        struct regmap *reg;
  24        struct clk_hw hw;
  25        unsigned long rate_min;
  26        unsigned long rate_max;
  27};
  28
  29#define to_vexpress_osc(osc) container_of(osc, struct vexpress_osc, hw)
  30
  31static unsigned long vexpress_osc_recalc_rate(struct clk_hw *hw,
  32                unsigned long parent_rate)
  33{
  34        struct vexpress_osc *osc = to_vexpress_osc(hw);
  35        u32 rate;
  36
  37        regmap_read(osc->reg, 0, &rate);
  38
  39        return rate;
  40}
  41
  42static long vexpress_osc_round_rate(struct clk_hw *hw, unsigned long rate,
  43                unsigned long *parent_rate)
  44{
  45        struct vexpress_osc *osc = to_vexpress_osc(hw);
  46
  47        if (WARN_ON(osc->rate_min && rate < osc->rate_min))
  48                rate = osc->rate_min;
  49
  50        if (WARN_ON(osc->rate_max && rate > osc->rate_max))
  51                rate = osc->rate_max;
  52
  53        return rate;
  54}
  55
  56static int vexpress_osc_set_rate(struct clk_hw *hw, unsigned long rate,
  57                unsigned long parent_rate)
  58{
  59        struct vexpress_osc *osc = to_vexpress_osc(hw);
  60
  61        return regmap_write(osc->reg, 0, rate);
  62}
  63
  64static struct clk_ops vexpress_osc_ops = {
  65        .recalc_rate = vexpress_osc_recalc_rate,
  66        .round_rate = vexpress_osc_round_rate,
  67        .set_rate = vexpress_osc_set_rate,
  68};
  69
  70
  71static int vexpress_osc_probe(struct platform_device *pdev)
  72{
  73        struct clk_lookup *cl = pdev->dev.platform_data; /* Non-DT lookup */
  74        struct clk_init_data init;
  75        struct vexpress_osc *osc;
  76        struct clk *clk;
  77        u32 range[2];
  78
  79        osc = devm_kzalloc(&pdev->dev, sizeof(*osc), GFP_KERNEL);
  80        if (!osc)
  81                return -ENOMEM;
  82
  83        osc->reg = devm_regmap_init_vexpress_config(&pdev->dev);
  84        if (IS_ERR(osc->reg))
  85                return PTR_ERR(osc->reg);
  86
  87        if (of_property_read_u32_array(pdev->dev.of_node, "freq-range", range,
  88                        ARRAY_SIZE(range)) == 0) {
  89                osc->rate_min = range[0];
  90                osc->rate_max = range[1];
  91        }
  92
  93        if (of_property_read_string(pdev->dev.of_node, "clock-output-names",
  94                        &init.name) != 0)
  95                init.name = dev_name(&pdev->dev);
  96
  97        init.ops = &vexpress_osc_ops;
  98        init.flags = CLK_IS_ROOT;
  99        init.num_parents = 0;
 100
 101        osc->hw.init = &init;
 102
 103        clk = clk_register(NULL, &osc->hw);
 104        if (IS_ERR(clk))
 105                return PTR_ERR(clk);
 106
 107        of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get, clk);
 108
 109        /* Only happens for non-DT cases */
 110        if (cl) {
 111                cl->clk = clk;
 112                clkdev_add(cl);
 113        }
 114
 115        dev_dbg(&pdev->dev, "Registered clock '%s'\n", init.name);
 116
 117        return 0;
 118}
 119
 120static struct of_device_id vexpress_osc_of_match[] = {
 121        { .compatible = "arm,vexpress-osc", },
 122        {}
 123};
 124
 125static struct platform_driver vexpress_osc_driver = {
 126        .driver = {
 127                .name = "vexpress-osc",
 128                .of_match_table = vexpress_osc_of_match,
 129        },
 130        .probe = vexpress_osc_probe,
 131};
 132
 133static int __init vexpress_osc_init(void)
 134{
 135        return platform_driver_register(&vexpress_osc_driver);
 136}
 137core_initcall(vexpress_osc_init);
 138