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