root/drivers/clk/mvebu/armada-37xx-tbg.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. tbg_get_mult
  2. tbg_get_div
  3. armada_3700_tbg_clock_probe
  4. armada_3700_tbg_clock_remove

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  * Marvell Armada 37xx SoC Time Base Generator clocks
   4  *
   5  * Copyright (C) 2016 Marvell
   6  *
   7  * Gregory CLEMENT <gregory.clement@free-electrons.com>
   8  */
   9 
  10 #include <linux/clk-provider.h>
  11 #include <linux/clk.h>
  12 #include <linux/io.h>
  13 #include <linux/of.h>
  14 #include <linux/of_address.h>
  15 #include <linux/platform_device.h>
  16 #include <linux/slab.h>
  17 
  18 #define NUM_TBG     4
  19 
  20 #define TBG_CTRL0               0x4
  21 #define TBG_CTRL1               0x8
  22 #define TBG_CTRL7               0x20
  23 #define TBG_CTRL8               0x30
  24 
  25 #define TBG_DIV_MASK            0x1FF
  26 
  27 #define TBG_A_REFDIV            0
  28 #define TBG_B_REFDIV            16
  29 
  30 #define TBG_A_FBDIV             2
  31 #define TBG_B_FBDIV             18
  32 
  33 #define TBG_A_VCODIV_SE         0
  34 #define TBG_B_VCODIV_SE         16
  35 
  36 #define TBG_A_VCODIV_DIFF       1
  37 #define TBG_B_VCODIV_DIFF       17
  38 
  39 struct tbg_def {
  40         char *name;
  41         u32 refdiv_offset;
  42         u32 fbdiv_offset;
  43         u32 vcodiv_reg;
  44         u32 vcodiv_offset;
  45 };
  46 
  47 static const struct tbg_def tbg[NUM_TBG] = {
  48         {"TBG-A-P", TBG_A_REFDIV, TBG_A_FBDIV, TBG_CTRL8, TBG_A_VCODIV_DIFF},
  49         {"TBG-B-P", TBG_B_REFDIV, TBG_B_FBDIV, TBG_CTRL8, TBG_B_VCODIV_DIFF},
  50         {"TBG-A-S", TBG_A_REFDIV, TBG_A_FBDIV, TBG_CTRL1, TBG_A_VCODIV_SE},
  51         {"TBG-B-S", TBG_B_REFDIV, TBG_B_FBDIV, TBG_CTRL1, TBG_B_VCODIV_SE},
  52 };
  53 
  54 static unsigned int tbg_get_mult(void __iomem *reg, const struct tbg_def *ptbg)
  55 {
  56         u32 val;
  57 
  58         val = readl(reg + TBG_CTRL0);
  59 
  60         return ((val >> ptbg->fbdiv_offset) & TBG_DIV_MASK) << 2;
  61 }
  62 
  63 static unsigned int tbg_get_div(void __iomem *reg, const struct tbg_def *ptbg)
  64 {
  65         u32 val;
  66         unsigned int div;
  67 
  68         val = readl(reg + TBG_CTRL7);
  69 
  70         div = (val >> ptbg->refdiv_offset) & TBG_DIV_MASK;
  71         if (div == 0)
  72                 div = 1;
  73         val = readl(reg + ptbg->vcodiv_reg);
  74 
  75         div *= 1 << ((val >>  ptbg->vcodiv_offset) & TBG_DIV_MASK);
  76 
  77         return div;
  78 }
  79 
  80 
  81 static int armada_3700_tbg_clock_probe(struct platform_device *pdev)
  82 {
  83         struct device_node *np = pdev->dev.of_node;
  84         struct clk_hw_onecell_data *hw_tbg_data;
  85         struct device *dev = &pdev->dev;
  86         const char *parent_name;
  87         struct resource *res;
  88         struct clk *parent;
  89         void __iomem *reg;
  90         int i, ret;
  91 
  92         hw_tbg_data = devm_kzalloc(&pdev->dev,
  93                                    struct_size(hw_tbg_data, hws, NUM_TBG),
  94                                    GFP_KERNEL);
  95         if (!hw_tbg_data)
  96                 return -ENOMEM;
  97         hw_tbg_data->num = NUM_TBG;
  98         platform_set_drvdata(pdev, hw_tbg_data);
  99 
 100         parent = clk_get(dev, NULL);
 101         if (IS_ERR(parent)) {
 102                 dev_err(dev, "Could get the clock parent\n");
 103                 return -EINVAL;
 104         }
 105         parent_name = __clk_get_name(parent);
 106         clk_put(parent);
 107 
 108         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 109         reg = devm_ioremap_resource(dev, res);
 110         if (IS_ERR(reg))
 111                 return PTR_ERR(reg);
 112 
 113         for (i = 0; i < NUM_TBG; i++) {
 114                 const char *name;
 115                 unsigned int mult, div;
 116 
 117                 name = tbg[i].name;
 118                 mult = tbg_get_mult(reg, &tbg[i]);
 119                 div = tbg_get_div(reg, &tbg[i]);
 120                 hw_tbg_data->hws[i] = clk_hw_register_fixed_factor(NULL, name,
 121                                                 parent_name, 0, mult, div);
 122                 if (IS_ERR(hw_tbg_data->hws[i]))
 123                         dev_err(dev, "Can't register TBG clock %s\n", name);
 124         }
 125 
 126         ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, hw_tbg_data);
 127 
 128         return ret;
 129 }
 130 
 131 static int armada_3700_tbg_clock_remove(struct platform_device *pdev)
 132 {
 133         int i;
 134         struct clk_hw_onecell_data *hw_tbg_data = platform_get_drvdata(pdev);
 135 
 136         of_clk_del_provider(pdev->dev.of_node);
 137         for (i = 0; i < hw_tbg_data->num; i++)
 138                 clk_hw_unregister_fixed_factor(hw_tbg_data->hws[i]);
 139 
 140         return 0;
 141 }
 142 
 143 static const struct of_device_id armada_3700_tbg_clock_of_match[] = {
 144         { .compatible = "marvell,armada-3700-tbg-clock", },
 145         { }
 146 };
 147 
 148 static struct platform_driver armada_3700_tbg_clock_driver = {
 149         .probe = armada_3700_tbg_clock_probe,
 150         .remove = armada_3700_tbg_clock_remove,
 151         .driver         = {
 152                 .name   = "marvell-armada-3700-tbg-clock",
 153                 .of_match_table = armada_3700_tbg_clock_of_match,
 154         },
 155 };
 156 
 157 builtin_platform_driver(armada_3700_tbg_clock_driver);

/* [<][>][^][v][top][bottom][index][help] */