linux/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Xilinx 'Clocking Wizard' driver
   4 *
   5 *  Copyright (C) 2013 - 2014 Xilinx
   6 *
   7 *  Sören Brinkmann <soren.brinkmann@xilinx.com>
   8 */
   9
  10#include <linux/platform_device.h>
  11#include <linux/clk.h>
  12#include <linux/clk-provider.h>
  13#include <linux/slab.h>
  14#include <linux/io.h>
  15#include <linux/of.h>
  16#include <linux/module.h>
  17#include <linux/err.h>
  18
  19#define WZRD_NUM_OUTPUTS        7
  20#define WZRD_ACLK_MAX_FREQ      250000000UL
  21
  22#define WZRD_CLK_CFG_REG(n)     (0x200 + 4 * (n))
  23
  24#define WZRD_CLKOUT0_FRAC_EN    BIT(18)
  25#define WZRD_CLKFBOUT_FRAC_EN   BIT(26)
  26
  27#define WZRD_CLKFBOUT_MULT_SHIFT        8
  28#define WZRD_CLKFBOUT_MULT_MASK         (0xff << WZRD_CLKFBOUT_MULT_SHIFT)
  29#define WZRD_DIVCLK_DIVIDE_SHIFT        0
  30#define WZRD_DIVCLK_DIVIDE_MASK         (0xff << WZRD_DIVCLK_DIVIDE_SHIFT)
  31#define WZRD_CLKOUT_DIVIDE_SHIFT        0
  32#define WZRD_CLKOUT_DIVIDE_MASK         (0xff << WZRD_DIVCLK_DIVIDE_SHIFT)
  33
  34enum clk_wzrd_int_clks {
  35        wzrd_clk_mul,
  36        wzrd_clk_mul_div,
  37        wzrd_clk_int_max
  38};
  39
  40/**
  41 * struct clk_wzrd:
  42 * @clk_data:           Clock data
  43 * @nb:                 Notifier block
  44 * @base:               Memory base
  45 * @clk_in1:            Handle to input clock 'clk_in1'
  46 * @axi_clk:            Handle to input clock 's_axi_aclk'
  47 * @clks_internal:      Internal clocks
  48 * @clkout:             Output clocks
  49 * @speed_grade:        Speed grade of the device
  50 * @suspended:          Flag indicating power state of the device
  51 */
  52struct clk_wzrd {
  53        struct clk_onecell_data clk_data;
  54        struct notifier_block nb;
  55        void __iomem *base;
  56        struct clk *clk_in1;
  57        struct clk *axi_clk;
  58        struct clk *clks_internal[wzrd_clk_int_max];
  59        struct clk *clkout[WZRD_NUM_OUTPUTS];
  60        unsigned int speed_grade;
  61        bool suspended;
  62};
  63
  64#define to_clk_wzrd(_nb) container_of(_nb, struct clk_wzrd, nb)
  65
  66/* maximum frequencies for input/output clocks per speed grade */
  67static const unsigned long clk_wzrd_max_freq[] = {
  68        800000000UL,
  69        933000000UL,
  70        1066000000UL
  71};
  72
  73static int clk_wzrd_clk_notifier(struct notifier_block *nb, unsigned long event,
  74                                 void *data)
  75{
  76        unsigned long max;
  77        struct clk_notifier_data *ndata = data;
  78        struct clk_wzrd *clk_wzrd = to_clk_wzrd(nb);
  79
  80        if (clk_wzrd->suspended)
  81                return NOTIFY_OK;
  82
  83        if (ndata->clk == clk_wzrd->clk_in1)
  84                max = clk_wzrd_max_freq[clk_wzrd->speed_grade - 1];
  85        else if (ndata->clk == clk_wzrd->axi_clk)
  86                max = WZRD_ACLK_MAX_FREQ;
  87        else
  88                return NOTIFY_DONE;     /* should never happen */
  89
  90        switch (event) {
  91        case PRE_RATE_CHANGE:
  92                if (ndata->new_rate > max)
  93                        return NOTIFY_BAD;
  94                return NOTIFY_OK;
  95        case POST_RATE_CHANGE:
  96        case ABORT_RATE_CHANGE:
  97        default:
  98                return NOTIFY_DONE;
  99        }
 100}
 101
 102static int __maybe_unused clk_wzrd_suspend(struct device *dev)
 103{
 104        struct clk_wzrd *clk_wzrd = dev_get_drvdata(dev);
 105
 106        clk_disable_unprepare(clk_wzrd->axi_clk);
 107        clk_wzrd->suspended = true;
 108
 109        return 0;
 110}
 111
 112static int __maybe_unused clk_wzrd_resume(struct device *dev)
 113{
 114        int ret;
 115        struct clk_wzrd *clk_wzrd = dev_get_drvdata(dev);
 116
 117        ret = clk_prepare_enable(clk_wzrd->axi_clk);
 118        if (ret) {
 119                dev_err(dev, "unable to enable s_axi_aclk\n");
 120                return ret;
 121        }
 122
 123        clk_wzrd->suspended = false;
 124
 125        return 0;
 126}
 127
 128static SIMPLE_DEV_PM_OPS(clk_wzrd_dev_pm_ops, clk_wzrd_suspend,
 129                         clk_wzrd_resume);
 130
 131static int clk_wzrd_probe(struct platform_device *pdev)
 132{
 133        int i, ret;
 134        u32 reg;
 135        unsigned long rate;
 136        const char *clk_name;
 137        struct clk_wzrd *clk_wzrd;
 138        struct resource *mem;
 139        struct device_node *np = pdev->dev.of_node;
 140
 141        clk_wzrd = devm_kzalloc(&pdev->dev, sizeof(*clk_wzrd), GFP_KERNEL);
 142        if (!clk_wzrd)
 143                return -ENOMEM;
 144        platform_set_drvdata(pdev, clk_wzrd);
 145
 146        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 147        clk_wzrd->base = devm_ioremap_resource(&pdev->dev, mem);
 148        if (IS_ERR(clk_wzrd->base))
 149                return PTR_ERR(clk_wzrd->base);
 150
 151        ret = of_property_read_u32(np, "speed-grade", &clk_wzrd->speed_grade);
 152        if (!ret) {
 153                if (clk_wzrd->speed_grade < 1 || clk_wzrd->speed_grade > 3) {
 154                        dev_warn(&pdev->dev, "invalid speed grade '%d'\n",
 155                                 clk_wzrd->speed_grade);
 156                        clk_wzrd->speed_grade = 0;
 157                }
 158        }
 159
 160        clk_wzrd->clk_in1 = devm_clk_get(&pdev->dev, "clk_in1");
 161        if (IS_ERR(clk_wzrd->clk_in1)) {
 162                if (clk_wzrd->clk_in1 != ERR_PTR(-EPROBE_DEFER))
 163                        dev_err(&pdev->dev, "clk_in1 not found\n");
 164                return PTR_ERR(clk_wzrd->clk_in1);
 165        }
 166
 167        clk_wzrd->axi_clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
 168        if (IS_ERR(clk_wzrd->axi_clk)) {
 169                if (clk_wzrd->axi_clk != ERR_PTR(-EPROBE_DEFER))
 170                        dev_err(&pdev->dev, "s_axi_aclk not found\n");
 171                return PTR_ERR(clk_wzrd->axi_clk);
 172        }
 173        ret = clk_prepare_enable(clk_wzrd->axi_clk);
 174        if (ret) {
 175                dev_err(&pdev->dev, "enabling s_axi_aclk failed\n");
 176                return ret;
 177        }
 178        rate = clk_get_rate(clk_wzrd->axi_clk);
 179        if (rate > WZRD_ACLK_MAX_FREQ) {
 180                dev_err(&pdev->dev, "s_axi_aclk frequency (%lu) too high\n",
 181                        rate);
 182                ret = -EINVAL;
 183                goto err_disable_clk;
 184        }
 185
 186        /* we don't support fractional div/mul yet */
 187        reg = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(0)) &
 188                    WZRD_CLKFBOUT_FRAC_EN;
 189        reg |= readl(clk_wzrd->base + WZRD_CLK_CFG_REG(2)) &
 190                     WZRD_CLKOUT0_FRAC_EN;
 191        if (reg)
 192                dev_warn(&pdev->dev, "fractional div/mul not supported\n");
 193
 194        /* register multiplier */
 195        reg = (readl(clk_wzrd->base + WZRD_CLK_CFG_REG(0)) &
 196                     WZRD_CLKFBOUT_MULT_MASK) >> WZRD_CLKFBOUT_MULT_SHIFT;
 197        clk_name = kasprintf(GFP_KERNEL, "%s_mul", dev_name(&pdev->dev));
 198        if (!clk_name) {
 199                ret = -ENOMEM;
 200                goto err_disable_clk;
 201        }
 202        clk_wzrd->clks_internal[wzrd_clk_mul] = clk_register_fixed_factor
 203                        (&pdev->dev, clk_name,
 204                         __clk_get_name(clk_wzrd->clk_in1),
 205                         0, reg, 1);
 206        kfree(clk_name);
 207        if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul])) {
 208                dev_err(&pdev->dev, "unable to register fixed-factor clock\n");
 209                ret = PTR_ERR(clk_wzrd->clks_internal[wzrd_clk_mul]);
 210                goto err_disable_clk;
 211        }
 212
 213        /* register div */
 214        reg = (readl(clk_wzrd->base + WZRD_CLK_CFG_REG(0)) &
 215                        WZRD_DIVCLK_DIVIDE_MASK) >> WZRD_DIVCLK_DIVIDE_SHIFT;
 216        clk_name = kasprintf(GFP_KERNEL, "%s_mul_div", dev_name(&pdev->dev));
 217        if (!clk_name) {
 218                ret = -ENOMEM;
 219                goto err_rm_int_clk;
 220        }
 221
 222        clk_wzrd->clks_internal[wzrd_clk_mul_div] = clk_register_fixed_factor
 223                        (&pdev->dev, clk_name,
 224                         __clk_get_name(clk_wzrd->clks_internal[wzrd_clk_mul]),
 225                         0, 1, reg);
 226        if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul_div])) {
 227                dev_err(&pdev->dev, "unable to register divider clock\n");
 228                ret = PTR_ERR(clk_wzrd->clks_internal[wzrd_clk_mul_div]);
 229                goto err_rm_int_clk;
 230        }
 231
 232        /* register div per output */
 233        for (i = WZRD_NUM_OUTPUTS - 1; i >= 0 ; i--) {
 234                const char *clkout_name;
 235
 236                if (of_property_read_string_index(np, "clock-output-names", i,
 237                                                  &clkout_name)) {
 238                        dev_err(&pdev->dev,
 239                                "clock output name not specified\n");
 240                        ret = -EINVAL;
 241                        goto err_rm_int_clks;
 242                }
 243                reg = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(2) + i * 12);
 244                reg &= WZRD_CLKOUT_DIVIDE_MASK;
 245                reg >>= WZRD_CLKOUT_DIVIDE_SHIFT;
 246                clk_wzrd->clkout[i] = clk_register_fixed_factor
 247                        (&pdev->dev, clkout_name, clk_name, 0, 1, reg);
 248                if (IS_ERR(clk_wzrd->clkout[i])) {
 249                        int j;
 250
 251                        for (j = i + 1; j < WZRD_NUM_OUTPUTS; j++)
 252                                clk_unregister(clk_wzrd->clkout[j]);
 253                        dev_err(&pdev->dev,
 254                                "unable to register divider clock\n");
 255                        ret = PTR_ERR(clk_wzrd->clkout[i]);
 256                        goto err_rm_int_clks;
 257                }
 258        }
 259
 260        kfree(clk_name);
 261
 262        clk_wzrd->clk_data.clks = clk_wzrd->clkout;
 263        clk_wzrd->clk_data.clk_num = ARRAY_SIZE(clk_wzrd->clkout);
 264        of_clk_add_provider(np, of_clk_src_onecell_get, &clk_wzrd->clk_data);
 265
 266        if (clk_wzrd->speed_grade) {
 267                clk_wzrd->nb.notifier_call = clk_wzrd_clk_notifier;
 268
 269                ret = clk_notifier_register(clk_wzrd->clk_in1,
 270                                            &clk_wzrd->nb);
 271                if (ret)
 272                        dev_warn(&pdev->dev,
 273                                 "unable to register clock notifier\n");
 274
 275                ret = clk_notifier_register(clk_wzrd->axi_clk, &clk_wzrd->nb);
 276                if (ret)
 277                        dev_warn(&pdev->dev,
 278                                 "unable to register clock notifier\n");
 279        }
 280
 281        return 0;
 282
 283err_rm_int_clks:
 284        clk_unregister(clk_wzrd->clks_internal[1]);
 285err_rm_int_clk:
 286        kfree(clk_name);
 287        clk_unregister(clk_wzrd->clks_internal[0]);
 288err_disable_clk:
 289        clk_disable_unprepare(clk_wzrd->axi_clk);
 290
 291        return ret;
 292}
 293
 294static int clk_wzrd_remove(struct platform_device *pdev)
 295{
 296        int i;
 297        struct clk_wzrd *clk_wzrd = platform_get_drvdata(pdev);
 298
 299        of_clk_del_provider(pdev->dev.of_node);
 300
 301        for (i = 0; i < WZRD_NUM_OUTPUTS; i++)
 302                clk_unregister(clk_wzrd->clkout[i]);
 303        for (i = 0; i < wzrd_clk_int_max; i++)
 304                clk_unregister(clk_wzrd->clks_internal[i]);
 305
 306        if (clk_wzrd->speed_grade) {
 307                clk_notifier_unregister(clk_wzrd->axi_clk, &clk_wzrd->nb);
 308                clk_notifier_unregister(clk_wzrd->clk_in1, &clk_wzrd->nb);
 309        }
 310
 311        clk_disable_unprepare(clk_wzrd->axi_clk);
 312
 313        return 0;
 314}
 315
 316static const struct of_device_id clk_wzrd_ids[] = {
 317        { .compatible = "xlnx,clocking-wizard" },
 318        { },
 319};
 320MODULE_DEVICE_TABLE(of, clk_wzrd_ids);
 321
 322static struct platform_driver clk_wzrd_driver = {
 323        .driver = {
 324                .name = "clk-wizard",
 325                .of_match_table = clk_wzrd_ids,
 326                .pm = &clk_wzrd_dev_pm_ops,
 327        },
 328        .probe = clk_wzrd_probe,
 329        .remove = clk_wzrd_remove,
 330};
 331module_platform_driver(clk_wzrd_driver);
 332
 333MODULE_LICENSE("GPL");
 334MODULE_AUTHOR("Soeren Brinkmann <soren.brinkmann@xilinx.com");
 335MODULE_DESCRIPTION("Driver for the Xilinx Clocking Wizard IP core");
 336